View Javadoc
1 /* 2 * Created on Dec 3, 2003 3 * AbstractFileInputAdapter.java 4 */ 5 package com.rhi.architecture.parc.adapter.file; 6 7 import java.io.BufferedReader; 8 import java.io.IOException; 9 import java.io.Reader; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.Collection; 13 import java.util.Properties; 14 15 import com.rhi.architecture.lang.StringUtils; 16 import com.rhi.architecture.logging.LogUtil; 17 import com.rhi.architecture.logging.Logger; 18 import com.rhi.architecture.parc.ProcessingException; 19 import com.rhi.architecture.parc.Record; 20 import com.rhi.architecture.parc.adapter.AbstractInputAdapter; 21 import com.rhi.architecture.resource.InitializationException; 22 23 /*** 24 * AbstractFileInputAdapter 25 * 26 * Should serve as a common base classes that all kinds of file input adapters can 27 * share. File input adapters can work with one fixed file or on a directory on 28 * polling basis or some other type. This base class should be flexible enough to 29 * cater for different file input adapters. 30 * 31 * @author Vaibhav Ranjangaonkar 32 * @version 1.0 33 */ 34 public abstract class AbstractFileInputAdapter extends AbstractInputAdapter { 35 36 private static Logger log = LogUtil.getLogger( AbstractFileInputAdapter.class ); 37 38 private static final String DELIMITER= "FlatFileInputAdapter.FLAT_FILE_DELIMITER"; 39 40 private static final String BATCH_SIZE= "FlatFileInputAdapter.FLAT_FILE_BATCH_SIZE"; 41 42 private String delim; // column delimiter 43 44 private int batchSize; // size of records to be read in one batch 45 46 /*** 47 * constructor 48 */ 49 public AbstractFileInputAdapter() { 50 super(); 51 } 52 53 /*** 54 * <p> 55 * Performs any resource initialization. 56 * <br>Initilizes following things: 57 * <ul> 58 * <li> Logger object 59 * <li> Flat file related information like 1) file name {@link #delim} 60 * 2) no. of records to be read from file in the one batch {@link #batchSize} 61 * <li> BufferedReader {@link #m_BufferReader} , to read the file line 62 * by line in different batches. 63 * </ul> 64 * </p> 65 * @param props <b>Properties</b> Property object, to store the 66 * initialization parameters. 67 * @exception <b>InitializationException</b> It throws the 68 * initialization exception, if it occurs during any of the 69 * above mentioned initialization. 70 * @since 1.0 71 */ 72 public void init( Properties props ) throws InitializationException { 73 super.init( props ); 74 initBatchSize( props ); 75 initDelimiter( props ); 76 } 77 78 /*** 79 * Reads the file one record at a time using readRecordString(), parses 80 * each record into column values and passes this list of colums to the 81 * method readRow(). Subclasses provide implementation of readRow() that 82 * takes this list of columns and return a class implementing Record interface. 83 * 84 * Although, default behaviour is to treat each line as a record, that may 85 * not be true always, subclasses can then extend this class using the hooks 86 * provided. 87 * 88 * @see readRecordString( String ) 89 * @see parseRecordString( String ) 90 * @see readRow( ArrayList ) 91 * 92 * @return <b>Collection</b> contains the list of generic record, read from flat file. 93 * @exception <b>ProcessingException</b> It throws the ProcessingException, 94 * if there is a error during the reading of afile. 95 * 96 * @since 1.0 97 */ 98 protected Collection loadBatch() throws ProcessingException { 99 log.debug( "AbstractFileInputAdapter : loadBatch()" ); 100 ArrayList records = new ArrayList(); 101 102 try { 103 int counter = 0; 104 // getBatchSize() is used instead of batchSize to give subclasses 105 // an opportunity to override batch size from properties if they want. 106 while ( ( counter < getBatchSize() ) || 107 ( getBatchSize() == 0 ) ) 108 { 109 String strRec = readRecordString(); 110 if (strRec == null) { 111 log.debug( "End of file: "); 112 break; 113 } 114 log.debug( "Line read from the file is : " + strRec ); 115 counter++; 116 117 String[] cols = parseRecordString( strRec ); 118 if ( cols == null ){ 119 continue; 120 } 121 // conversion to ArrayList is needed for backward compatibility as 122 // signature of readRow() requires an ArrayList instead of a String[] 123 // that signature can't be changed now as old code will break due to that. 124 ArrayList columns = new ArrayList( Arrays.asList( cols ) ); 125 126 Record record = readRow( columns ); 127 if ( record == null ) { 128 continue; 129 } 130 // doesn't add null or errored records to the list of records to return. 131 if ( !record.isErrored() ) { 132 records.add( record ); 133 } 134 else { 135 log.error( "Invalid record : " + record ); 136 super.addToErrorChannel( record ); 137 } 138 } 139 } 140 catch ( IOException ioe ) { 141 log.error( "Error while reading a line from the file" ); 142 throw new ProcessingException( 143 "Error while reading a line from the file", ioe ); 144 } 145 return records; 146 } 147 148 /*** 149 * Hook provided for subclasses to change the way a record is read from a file. 150 * Default behaviour is to treat each line as a record and read that line. 151 * But, for some cases, multiple lines can form one record, this method should 152 * then be overriden by the subclass and appropriate logic should be provided. 153 * 154 * @return 155 * @throws IOException 156 */ 157 protected String readRecordString() throws IOException { 158 return getFileReader().readLine(); 159 } 160 161 /*** 162 * Hook provided for subclasses to control the way given string is parsed to 163 * extract columns. Default behaviour is to split the string around a delimiter 164 * configured or returned by the getDelimiter() method of the subclass. But, this 165 * may not suit some classes that have different requirement to extract columns 166 * from a given record string. An example for this could be support for comments 167 * in the input file. Thus, a subclass can override this method and return null 168 * if 'record' string is starting with a comment character ( say, '#' ). 169 * 170 * @param record 171 * @return parsed record string 172 */ 173 protected String[] parseRecordString( String record ){ 174 return StringUtils.parseString( record, getDelimiter() ); 175 } 176 177 /*** 178 * Returns batchSize configured through properties, if batch size needs to provided 179 * by other means, then this method should be overridden. 180 * 181 * @return Returns the batchSize. 182 */ 183 protected int getBatchSize() { 184 return batchSize; 185 } 186 187 /*** 188 * Returns delimiter configured through properties, if delimiter needs to provided 189 * by other means, then this method should be overridden. 190 * 191 * @return Returns the delimiter. 192 */ 193 protected String getDelimiter() { 194 return delim; 195 } 196 197 /*** 198 * Details of how to obtain a reader to read records are left to the 199 * concrete classes to provide flexibility. This reader is used in 200 * loadBatch() to read record strings. 201 * 202 * @return Returns the fileReader. 203 */ 204 protected abstract BufferedReader getFileReader(); 205 206 /*** 207 * Extract the attributes from the ArrayList and return a Record object. 208 * When overriding this method, create the appropriate Record type for 209 * the interface & return it through the generic Record interface. 210 * 211 * @param ArrayList 212 * @exception SQLException 213 * 214 * @return Record 215 * 216 * @since 1.0 217 */ 218 protected abstract Record readRow(ArrayList al) throws ProcessingException; 219 220 /*** 221 * Convenience method to avoid code duplication in sub-classes. Probably every 222 * subclass will have to write its own close reader operation, so this should 223 * serve that need and keep it one place. Can be moved to a common file utils 224 * kind of class when we have one. 225 * 226 * Closes the reader instance if one exists. Made protected so 227 * that subclasses can reuse this code. 228 */ 229 public static void closeReader( Reader reader ) { 230 try { 231 if (reader != null) { 232 reader.close(); 233 } 234 } 235 catch (IOException exIO) { 236 log.error( "Error while closing the file stream.", exIO ); 237 } 238 } 239 240 /*** 241 * Initializes file delimiter property. 242 * @param props 243 * @throws InitializationException 244 */ 245 private void initDelimiter(Properties props) throws InitializationException { 246 delim = props.getProperty(DELIMITER, "N/A"); 247 if (delim.equals("N/A")) { 248 throw new InitializationException( 249 "FlatFileInputAdapter initialization error. " 250 + DELIMITER 251 + " Delimiter Pattern not found."); 252 } 253 } 254 255 /*** 256 * Initializes batch size. 257 * @param props 258 * @throws InitializationException 259 */ 260 private void initBatchSize(Properties props) throws InitializationException { 261 String strTempBatchSize = props.getProperty( BATCH_SIZE, "N/A" ); 262 263 if (strTempBatchSize.equals("N/A")) { 264 strTempBatchSize = "0"; 265 } 266 try { 267 batchSize = Integer.parseInt( strTempBatchSize ); 268 } 269 catch (NumberFormatException exNumberFormat) { 270 log.error( 271 "Batch size value is not proper. Batch size read is " 272 + strTempBatchSize ); 273 throw new InitializationException( 274 "Batch size value is not proper. Batch size :" 275 + strTempBatchSize, 276 exNumberFormat); 277 } 278 } 279 280 }

This page was automatically generated by Maven