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