Java tutorial
/** * Copyright: 2010 Regents of the University of Hawaii and the * School of Ocean and Earth Science and Technology * Purpose: To convert lines of an ASCII data file into RBNB Data Turbine * frames for archival and realtime access. * Authors: Christopher Jones * * $HeadURL$ * $LastChangedDate$ * $LastChangedBy$ * $LastChangedRevision$ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.hawaii.soest.kilonalu.utilities; import com.rbnb.sapi.ChannelMap; import com.rbnb.sapi.Sink; import com.rbnb.sapi.Source; import com.rbnb.sapi.SAPIException; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.InterruptedException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.cli.Options; import org.apache.commons.cli.CommandLine; import org.apache.log4j.Logger; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.PropertyConfigurator; import org.nees.rbnb.RBNBBase; import org.nees.rbnb.RBNBSource; /** * A class used to harvest ASCII data lines from a file. * The data are converted into RBNB frames and pushed into the RBNB DataTurbine * real time server. This class extends org.nees.rbnb.RBNBSource, which in * turn extends org.nees.rbnb.RBNBBase, and therefore follows the API * conventions found in the org.nees.rbnb code. * */ public class FileSource extends RBNBSource { /* * A default archive mode for the given source connection to the RBNB server. * Valid modes include 'append', 'create', 'load' and 'none'. */ private final String DEFAULT_ARCHIVE_MODE = "append"; /* * The mode in which the source interacts with the RBNB archive. Valid modes * include 'append', 'create', 'load' and 'none', however, Kilo Nalu * instruments should append to an archive, which will create one if none * exist. * * @see setArchiveMode() * @see getArchiveMode() */ private String archiveMode = DEFAULT_ARCHIVE_MODE; /* A default RBNB channel name for the given source instrument */ private String DEFAULT_RBNB_CHANNEL = "DecimalASCIISampleData"; /* The name of the RBNB channel for this data stream */ private String rbnbChannelName = DEFAULT_RBNB_CHANNEL; /* A default source address for the given source email server */ private final String DEFAULT_FILE_NAME = "/tmp/data.txt"; /* A file name for the given data source file */ private String fileName = DEFAULT_FILE_NAME; /* The default IP address or DNS name of the RBNB server */ private static final String DEFAULT_SERVER_NAME = "localhost"; /* The default TCP port of the RBNB server */ private static final int DEFAULT_SERVER_PORT = 3333; /* The IP address or DNS name of the RBNB server */ private String serverName = DEFAULT_SERVER_NAME; /* The default TCP port of the RBNB server */ private int serverPort = DEFAULT_SERVER_PORT; /* The address and port string for the RBNB server */ private String server = serverName + ":" + serverPort; /* The default date format for the timestamp in the data sample string */ private static final SimpleDateFormat defaultDateFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss"); /* The default sample line delimiter separating columns of data */ private static final String DEFAULT_DELIMITER = ","; /* The delimiter separating variables in the sample line */ private static String delimiter = DEFAULT_DELIMITER; /* A list of date format patterns to be applied to designated date/time fields */ private List<String> dateFormats = null; /* A one-based list of Integers corresponding to the observation date/time field indices */ private List<Integer> dateFields = null; /* The instance of TimeZone to use when parsing dates */ private TimeZone tz; /* The buffered reader data representing the data file stream */ private BufferedReader fileReader; /* The pattern used to identify data lines */ private Pattern dataPattern; /* The timezone that the data samples are taken in as a string (UTC, HST, etc.) */ private String timezone = "UTC"; /* The default log configuration file location */ private final String DEFAULT_LOG_CONFIGURATION_FILE = "lib/log4j.properties"; /* The log configuration file location */ private String logConfigurationFile = DEFAULT_LOG_CONFIGURATION_FILE; /* The Logger instance used to log system messages */ private static Logger logger = Logger.getLogger(FileSource.class); /* The state used to track the data processing */ protected int state = 0; /* A boolean indicating if we are ready to stream (connected) */ private boolean readyToStream = false; /* The thread used for streaming data */ private Thread streamingThread; /* The polling interval (millis) used to check for new lines in the data file */ private final int POLL_INTERVAL = 10000; /* * An internal Thread setting used to specify how long, in milliseconds, the * execution of the data streaming Thread should wait before re-executing * * @see execute() */ private final int RETRY_INTERVAL = 5000; /** * Constructor - create an empty instance of the FileSource object, using * default values for the RBNB server name and port, source instrument name, * archive mode, archive frame size, and cache frame size. */ public FileSource() { } /** * A method that executes the reading of data from the data file to the RBNB * server after all configuration of settings, connections to hosts, and * thread initiatizing occurs. This method contains the detailed code for * reading the data and interpreting the data files. */ protected boolean execute() { logger.debug("FileSource.execute() called."); boolean failed = true; // indicates overall success of execute() // do not execute the stream if there is no connection if (!isConnected()) return false; try { // open the data file for monitoring FileReader reader = new FileReader(new File(getFileName())); this.fileReader = new BufferedReader(reader); // add channels of data that will be pushed to the server. // Each sample will be sent to the Data Turbine as an rbnb frame. int channelIndex = 0; ChannelMap rbnbChannelMap = new ChannelMap(); // used to insert channel data ChannelMap registerChannelMap = new ChannelMap(); // used to register channels // add the DecimalASCIISampleData channel to the channelMap channelIndex = registerChannelMap.Add(getRBNBChannelName()); registerChannelMap.PutUserInfo(channelIndex, "units=none"); // and register the RBNB channels getSource().Register(registerChannelMap); registerChannelMap.Clear(); // on execute(), query the DT to find the timestamp of the last sample inserted // and be sure the following inserts are after that date ChannelMap requestMap = new ChannelMap(); int entryIndex = requestMap.Add(getRBNBClientName() + "/" + getRBNBChannelName()); logger.debug("Request Map: " + requestMap.toString()); Sink sink = new Sink(); sink.OpenRBNBConnection(getServer(), "lastEntrySink"); sink.Request(requestMap, 0., 1., "newest"); ChannelMap responseMap = sink.Fetch(5000); // get data within 5 seconds // initialize the last sample date Date initialDate = new Date(); long lastSampleTimeAsSecondsSinceEpoch = initialDate.getTime() / 1000L; logger.debug("Initialized the last sample date to: " + initialDate.toString()); logger.debug("The last sample date as a long is: " + lastSampleTimeAsSecondsSinceEpoch); if (responseMap.NumberOfChannels() == 0) { // set the last sample time to 0 since there are no channels yet lastSampleTimeAsSecondsSinceEpoch = 0L; logger.debug("Resetting the last sample date to the epoch: " + (new Date(lastSampleTimeAsSecondsSinceEpoch * 1000L)).toString()); } else if (responseMap.NumberOfChannels() > 0) { lastSampleTimeAsSecondsSinceEpoch = new Double(responseMap.GetTimeStart(entryIndex)).longValue(); logger.debug("There are existing channels. Last sample time: " + (new Date(lastSampleTimeAsSecondsSinceEpoch * 1000L)).toString()); } sink.CloseRBNBConnection(); // poll the data file for new lines of data and insert them into the RBNB while (true) { String line = fileReader.readLine(); if (line == null) { this.streamingThread.sleep(POLL_INTERVAL); } else { // test the line for the expected data pattern Matcher matcher = this.dataPattern.matcher(line); // if the line matches the data pattern, insert it into the DataTurbine if (matcher.matches()) { logger.debug("This line matches the data line pattern: " + line); Date sampleDate = getSampleDate(line); if (this.dateFormats == null || this.dateFields == null) { logger.warn("Using the default datetime format and field for sample data. " + "Use the -f and -d options to explicitly set date time fields."); } logger.debug("Sample date is: " + sampleDate.toString()); // convert the sample date to seconds since the epoch long sampleTimeAsSecondsSinceEpoch = (sampleDate.getTime() / 1000L); // only insert samples newer than the last sample seen at startup // and that are not in the future (> 1 hour since the CTD clock // may have drifted) Calendar currentCal = Calendar.getInstance(); this.tz = TimeZone.getTimeZone(this.timezone); currentCal.setTimeZone(this.tz); currentCal.add(Calendar.HOUR, 1); Date currentDate = currentCal.getTime(); if (lastSampleTimeAsSecondsSinceEpoch < sampleTimeAsSecondsSinceEpoch && sampleTimeAsSecondsSinceEpoch < currentDate.getTime() / 1000L) { // add the sample timestamp to the rbnb channel map //registerChannelMap.PutTime(sampleTimeAsSecondsSinceEpoch, 0d); rbnbChannelMap.PutTime((double) sampleTimeAsSecondsSinceEpoch, 0d); // then add the data line to the channel map channelIndex = rbnbChannelMap.Add(getRBNBChannelName()); rbnbChannelMap.PutMime(channelIndex, "text/plain"); rbnbChannelMap.PutDataAsString(channelIndex, line + "\r\n"); // be sure we are connected int numberOfChannelsFlushed = 0; try { //insert into the DataTurbine numberOfChannelsFlushed = getSource().Flush(rbnbChannelMap, true); // in the event we just lost the network, sleep, try again while (numberOfChannelsFlushed < 1) { logger.debug("No channels flushed, trying again in 10 seconds."); Thread.sleep(10000L); numberOfChannelsFlushed = getSource().Flush(rbnbChannelMap, true); } } catch (SAPIException sapie) { // reconnect if an exception is thrown on Flush() logger.debug("Error while flushing the source: " + sapie.getMessage()); logger.debug("Disconnecting from the DataTurbine."); disconnect(); logger.debug("Connecting to the DataTurbine."); connect(); getSource().Flush(rbnbChannelMap); // in the event we just lost the network, sleep, try again while (numberOfChannelsFlushed < 1) { logger.debug("No channels flushed, trying again in 10 seconds."); Thread.sleep(10000L); numberOfChannelsFlushed = getSource().Flush(rbnbChannelMap, true); } } // reset the last sample time to the sample just inserted lastSampleTimeAsSecondsSinceEpoch = sampleTimeAsSecondsSinceEpoch; logger.debug("Last sample time is now: " + (new Date(lastSampleTimeAsSecondsSinceEpoch * 1000L).toString())); logger.info(getRBNBClientName() + " Sample sent to the DataTurbine: " + line.trim()); rbnbChannelMap.Clear(); } else { logger.info("The current line is earlier than the last entry " + "in the Data Turbine or is a date in the future. " + "Skipping it. The line was: " + line); } } else { logger.info("The current line doesn't match an expected " + "data line pattern. Skipping it. The line was: " + line); } } // end if() } // end while } catch (ParseException pe) { logger.info( "There was a problem parsing the sample date string. " + "The message was: " + pe.getMessage()); try { this.fileReader.close(); } catch (IOException ioe2) { logger.info( "There was a problem closing the data file. The " + "message was: " + ioe2.getMessage()); } failed = true; return !failed; } catch (SAPIException sapie) { logger.info("There was a problem communicating with the DataTurbine. " + "The message was: " + sapie.getMessage()); try { this.fileReader.close(); } catch (IOException ioe2) { logger.info( "There was a problem closing the data file. The " + "message was: " + ioe2.getMessage()); } failed = true; return !failed; } catch (InterruptedException ie) { logger.info( "There was a problem while polling the data file. The " + "message was: " + ie.getMessage()); try { this.fileReader.close(); } catch (IOException ioe2) { logger.info( "There was a problem closing the data file. The " + "message was: " + ioe2.getMessage()); } failed = true; return !failed; } catch (IOException ioe) { logger.info("There was a problem opening the data file. " + "The message was: " + ioe.getMessage()); failed = true; return !failed; } } /** * A method that returns the name of the RBNB channel that contains the * streaming data from this instrument */ public String getRBNBChannelName() { return this.rbnbChannelName; } /** * A method that gets the file name of the source data file * * @return fileName - the name of the source data file */ public String getFileName() { return this.fileName; } /** * A method that returns the versioning info for this file. In this case, * it returns a String that includes the Subversion LastChangedDate, * LastChangedBy, LastChangedRevision, and HeadURL fields. */ public String getCVSVersionString() { return ("$LastChangedDate$" + "$LastChangedBy$" + "$LastChangedRevision$" + "$HeadURL$"); } /** * A method that returns true if the RBNB connection is established * and if the data streaming Thread has been started */ public boolean isRunning() { // return the connection status and the thread status return (isConnected() && readyToStream); } /* * A method that runs the data streaming work performed by the execute() * by handling execution problems and continuously trying to re-execute after * a specified retry interval for the thread. */ private void runWork() { // handle execution problems by retrying if execute() fails boolean retry = true; while (retry) { // connect to the RBNB server if (connect()) { // run the data streaming code retry = !execute(); } disconnect(); if (retry) { try { Thread.sleep(RETRY_INTERVAL); } catch (Exception e) { logger.info("There was an execution problem. Retrying. Message is: " + e.getMessage()); } } } // stop the streaming when we are done stop(); } /** * The main method for running the code * @ param args[] the command line list of string arguments, none are needed */ public static void main(String args[]) { /* args = new String[]{ "-F", "${REALTIME_DATA}/test/resources/edu/hawaii/soest/kilonalu/utilities/NS01-example-data.txt", "-e", "# *.*, *.*, *.*, *.*, *.*, *.*, *\\\\d{2} [A-Z][a-z][a-z] *\\\\d{4} *\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\s*", "-S", "TEST01_001CTDXXXXR00", "-C", "DecimalASCIISampleData", "-d", "yyyy-MM-dd HH:mm:ss", "-f", "7", "-s", "127.0.0.1", "-p", "3333", "-z", "126000", "-Z", "31536000" }; */ try { // create a new instance of the FileSource object, and parse the command // line arguments as settings for this instance final FileSource fileSource = new FileSource(); // Handle ctrl-c's and other abrupt death signals to the process Runtime.getRuntime().addShutdownHook(new Thread() { // stop the streaming process public void run() { fileSource.disconnect(); } }); // Set up a simple logger that logs to the console PropertyConfigurator.configure(fileSource.getLogConfigurationFile()); // parse the commandline arguments to configure the connection, then // start the streaming connection between the source and the RBNB server. if (fileSource.parseArgs(args)) { fileSource.start(); } } catch (Exception e) { logger.info("Error in main(): " + e.getMessage()); e.printStackTrace(); } } /** * A method that sets the RBNB channel name of the source instrument's data * stream * * @param channelName the name of the RBNB channel being streamed */ public void setChannelName(String channelName) { this.rbnbChannelName = channelName; } /** * A method that sets the command line arguments for this class. This method * calls the <code>RBNBSource.setBaseArgs()</code> method. * * @param command The CommandLine object being passed in from the command */ protected boolean setArgs(CommandLine command) { // first set the base arguments that are included on the command line if (!setBaseArgs(command)) { return false; } // add command line arguments here // handle the -F option if (command.hasOption("F")) { String fileName = command.getOptionValue("F"); if (fileName != null) { setFileName(fileName); } } // handle the -e option if (command.hasOption("e")) { String expression = command.getOptionValue("e"); if (expression != null) { setPattern(expression); } } // handle the -C option if (command.hasOption("C")) { String channelName = command.getOptionValue("C"); if (channelName != null) { setChannelName(channelName); } } // handle the -s option if (command.hasOption('s')) { String serverName = command.getOptionValue('s'); if (serverName != null) setServerName(serverName); } // handle the -t option if (command.hasOption("t")) { String timezone = command.getOptionValue("t"); if (timezone != null) { setTimezone(timezone); } } // handle the -d option, requires the -f option if (command.hasOption("d")) { if (!command.hasOption("f")) { System.out.println("The -f option is required to identify which " + "fields in the sample correspond to observation dates/times."); return false; } List<String> formatList = new ArrayList<String>(); String dateFormats = command.getOptionValue("d"); if (dateFormats != null) { String[] formats = dateFormats.split(","); for (String format : formats) { try { SimpleDateFormat f = new SimpleDateFormat(format); formatList.add(format); } catch (Exception e) { System.out .println("The date format " + format + " is not a proper date or time format. See " + "http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html" + " for allowable character patterns."); e.printStackTrace(); return false; } } setDateFormats(formatList); } } // handle the -d option, requires the -f option if (command.hasOption("D")) { String delim = command.getOptionValue("D"); try { Pattern pattern = Pattern.compile(delim); } catch (PatternSyntaxException e) { System.out.println( "The delimiter format " + delim + " is not a proper regular expression syntax. See " + "http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html" + " for allowable character patterns."); e.printStackTrace(); return false; } setDelimiter(delim); } // handle the -f option if (command.hasOption("f")) { if (!command.hasOption("d")) { System.out.println("The -d' option is required to identify the " + "formats to be applied to date/time fields in the sample."); return false; } List<Integer> dateFieldList = new ArrayList<Integer>(); String dateFields = command.getOptionValue("f"); String[] fields = dateFields.split(","); for (String field : fields) { try { Integer fieldIndex = new Integer(field); dateFieldList.add(fieldIndex); if (field.equals("0")) { System.out.println("The list of date fields must start " + "with 1 (one), not 0 (zero)."); return false; } } catch (NumberFormatException e) { System.out.println("The date field " + field + " is not a proper field index number. Provide a " + "comma-separated list of numbers, not including zero, " + "that correspond to observation date, time, or datetime " + "fields in the sample data. For instance, if the " + "observation timestamp was in column one of the sample, " + "just use '-f 1'. If column one holds the date, and column " + "two holds the time, use '-f 1,2'"); } setDateFields(dateFieldList); } } // handle the -p option if (command.hasOption('p')) { String serverPort = command.getOptionValue('p'); if (serverPort != null) { try { setServerPort(Integer.parseInt(serverPort)); } catch (NumberFormatException nf) { System.out.println( "Please enter a numeric value for -p (server port). " + serverPort + " is not valid."); return false; } } } return true; } /** * A method that sets the delimiter between variables in a sample string * * @param delim the delimiter string as a Java regular expression */ public void setDelimiter(String delim) { this.delimiter = delim; } /** * A method that sets the file name of the source data file * * @param fileName - the name of the source data file */ public void setFileName(String fileName) { this.fileName = fileName; } /** * A method that sets the regular expression pattern for matching data lines * * @param pattern - the pattern string used to match data lines */ public void setPattern(String pattern) { this.dataPattern = Pattern.compile(pattern); } /** * A method that sets the command line options for this class. This method * calls the <code>RBNBSource.setBaseOptions()</code> method in order to set * properties such as the sourceHostName, sourceHostPort, serverName, and * serverPort. */ protected Options setOptions() { Options options = setBaseOptions(new Options()); // Note: // Command line options already provided by RBNBBase include: // -h "Print help" // -s "RBNB Server Hostname" // -p "RBNB Server Port Number" // -S "RBNB Source Name" // -v "Print Version information" // Command line options already provided by RBNBSource include: // -z "Cache size" // -Z "Archive size" // add command line options here options.addOption("F", true, "Data source file name e.g. " + getFileName()); options.addOption("e", true, "regular expression for data line matching, e.g \"*[0-9][0-9]\" "); options.addOption("C", true, "RBNB source channel name e.g. " + getRBNBChannelName()); options.addOption("s", true, "RBNB Server Hostname"); options.addOption("t", true, "Timezone indicator (UTC, HST, EDT, etc.)"); options.addOption("d", true, "Date formats as a comma separated list(yyyy-MM-dd,HH:mm:ss)"); options.addOption("D", true, "Delimiter between variables as a regular expression (\",\", \"\\s+\")"); options.addOption("f", true, "Date fields as a one-based comma separated list(1,2)"); options.addOption("p", true, "RBNB Server Port Number"); return options; } /** * A method that starts the streaming of data lines from the source file to * the RBNB server. */ public boolean start() { // return false if the streaming is running if (isRunning()) { return false; } // reset the connection to the RBNB server if (isConnected()) { disconnect(); } connect(); // return false if the connection fails if (!isConnected()) { return false; } // begin the streaming thread to the source startThread(); return true; } /** * A method that creates and starts a new Thread with a run() method that * begins processing the data streaming from the source file. */ private void startThread() { // build the runnable class and implement the run() method Runnable runner = new Runnable() { public void run() { runWork(); } }; // build the Thread and start it, indicating that it has been started readyToStream = true; streamingThread = new Thread(runner, "StreamingThread"); streamingThread.start(); } /** * A method that stops the streaming of data between the source file and * the RBNB server. */ public boolean stop() { // return false if the thread is not running if (!isRunning()) { return false; } // stop the thread and disconnect from the RBNB server stopThread(); disconnect(); return true; } /** * A method that interrupts the thread created in startThread() */ private void stopThread() { // set the streaming status to false and stop the Thread readyToStream = false; streamingThread.interrupt(); } /** * A method that gets the log configuration file location * * @return logConfigurationFile the log configuration file location */ public String getLogConfigurationFile() { return this.logConfigurationFile; } /** * A method that sets the log configuration file name * * @param logConfigurationFile the log configuration file name */ public void setLogConfigurationFile(String logConfigurationFile) { this.logConfigurationFile = logConfigurationFile; } /** * A method that gets the timezone string * * @return timezone the timezone string (like "UTC", "HST", "PONT" etc.) */ public String getTimezone() { return this.timezone; } /** * A method that sets the timezone string * * @param timezone the timezone string (like "UTC", "HST", "PONT" etc.) */ public void setTimezone(String timezone) { this.timezone = timezone; } /** * Get the list of date formats that correspond to date and/or time fields in * the sample data. For instance, column one of the sample may be a timestamp * formatted as "YYYY-MM-DD HH:MM:SS". Alternatively, columns one and two may * be a date and a time, such as "YYYY-MM-DD,HH:MM:SS". * @return the dateFormats */ public List<String> getDateFormats() { return dateFormats; } /** * Set the list of date formats that correspond to date and/or time fields in * the sample data. For instance, column one of the sample may be a timestamp * formatted as "YYYY-MM-DD HH:MM:SS". Alternatively, columns one and two may * be a date and a time, such as "YYYY-MM-DD,HH:MM:SS". * @param dateFormats the dateFormats to set */ public void setDateFormats(List<String> dateFormats) { this.dateFormats = dateFormats; } /** * Get the list of Integers that correspond with field indices of the * sample observation's date, time, or datetime columns. * * @return the dateFields */ public List<Integer> getDateFields() { return dateFields; } /** * Set the list of Integers that correspond with field indices of the * sample observation's date, time, or datetime columns. The list must be * one-based, such as '1,2'. * @param dateFields the dateFields to set */ public void setDateFields(List<Integer> dateFields) { this.dateFields = dateFields; } /** * return the sample observation date given minimal sample metadata */ public Date getSampleDate(String line) throws ParseException { /* * Date time formats and field locations are highly dependent on instrument * output settings. The -d and -f options are used to set dateFormats and dateFields * * this.dateFormats will look something like {"yyyy-MM-dd", "HH:mm:ss"} or * {"yyyy-MM-dd HH:mm:ss"} or {"yyyy", "MM", "dd", "HH", "mm", "ss"} * * this.dateFields will also look something like {1,2} or {1} or {1, 2, 3, 4, 5, 6} * * NS01 sample: * # 26.1675, 4.93111, 0.695, 0.1918, 0.1163, 31.4138, 09 Dec 2012 15:46:55 * NS03 sample: * # 25.4746, 5.39169, 0.401, 35.2570, 09 Dec 2012, 15:44:36 */ // extract the date from the data line SimpleDateFormat dateFormat; String dateFormatStr = ""; String dateString = ""; String[] columns = line.trim().split(this.delimiter); logger.debug("Delimiter is: " + this.delimiter); logger.debug(Arrays.toString(columns)); // build the total date format from the individual fields listed in dateFields int index = 0; for (Integer dateField : this.dateFields) { dateFormatStr += this.dateFormats.get(index); //zero-based list dateString += columns[dateField.intValue() - 1].trim(); //zero-based list index++; } logger.debug("Using date format string: " + dateFormatStr); logger.debug("Using date string : " + dateString); this.tz = TimeZone.getTimeZone(this.timezone); if (this.dateFormats == null || this.dateFields == null) { logger.warn("Using the defaault datetime field for sample data. " + "Use the -f and -d options to explicitly set date time fields."); dateFormat = this.defaultDateFormat; } // init the date formatter dateFormat = new SimpleDateFormat(dateFormatStr); dateFormat.setTimeZone(this.tz); // parse the date string Date sampleDate = dateFormat.parse(dateString.trim()); return sampleDate; } }