/******************************************************************************* * 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.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ebay.jetstream.config.ConfigUtils; import com.ebay.jetstream.event.EventException; /** * This class was and should be part of FileChannelAddress or a base class of InbounfFileChannel * if we follow "expert" pattern. However, * to make the "address" simple, we move some implementation * details from the "address" class into this "helper" class. * * This class will not do any sanity checking which is performed * by the "address" class already. * * @author gjin */ public class InboundFileChannelHelper { private FileChannelAddress m_address; private BufferedReader m_br; /*inbound only: the reader associated with the local file */ private boolean m_eof = false; /*inbound only */ private FileOutputStream m_fos = null; /* for outbound */ private AtomicLong m_totalWrites = new AtomicLong(); private AtomicBoolean m_reportFull = new AtomicBoolean(); private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.event.channel.file"); private AtomicLong m_errorLoggingRate = new AtomicLong(5000); /* log every 5000 errors initially*/ private AtomicLong m_numberOfErrors = new AtomicLong(); /*** * */ public InboundFileChannelHelper(FileChannelAddress address) { m_address = address; if (m_address == null) { throw new IllegalArgumentException("the channel address must be no null"); } } public FileChannelAddress getAddress() { return m_address; } /* open the file */ public void openForRead() throws EventException { if (m_br != null) { LOGGER.info( "try to open an event file " + m_address.getPathname() + " which was open", "OpenFileTwice"); return; /* open already */ } File f = new File(ConfigUtils.getInitialPropertyExpanded(m_address.getPathname())); if (!f.isFile() || !f.canRead()) { throw new EventException("Filename=" + m_address.getPathname() + " is not a file or not readable"); } try { m_br = new BufferedReader(new InputStreamReader(new FileInputStream(m_address.getPathname()))); } catch (FileNotFoundException e) { throw new EventException("open file: " + m_address.getPathname(), e); } m_eof= false; } /**inbound only */ public void closeForRead() throws EventException { if (m_br == null) { LOGGER.info( "try to close an event file " + m_address.getPathname() + " which was not open", "CloseUnOpenFile"); return; /* close already or never open*/ } try { m_br.close(); } catch (IOException e) { throw new EventException("close file: " + m_address.getPathname(), e); } m_br = null; } /*** * inbound * @return null when file is not open, or running into IO exception, EOF, bad records */ public Map<String, Object> getNextEventMap() { /***TODO: what is event type, ID? * */ HashMap<String, Object> event = new HashMap<String, Object>(); if (m_br == null) { if (m_numberOfErrors.getAndIncrement() % m_errorLoggingRate.get() ==0) { LOGGER.error( "the event file is not open"); } return null; } if (m_eof) { if (m_numberOfErrors.getAndIncrement() % m_errorLoggingRate.get() ==0) { LOGGER.error( "the event file has reached the end"); } return null; } String line; try { line = m_br.readLine(); } catch (IOException e) { /* report ERROR */ LOGGER.error( e.getLocalizedMessage(), e); return null; } if (line== null) { /* report eof--- not an error */ LOGGER.warn( "file reaches the end"); m_eof = true; return null; } if (parseEvent(event, line)) { return event; } else { return null; } } private boolean parseEvent(Map<String, Object> event, String s) { Long longObj = 0L; Integer intObj = 0; Float floatObj = 0.0f; String[] columns = s.split(m_address.getColumnDelimiter(), -1); if (columns.length != m_address.getColumnNameArray().length) { /* the data has either more or less columns */ LOGGER.error( "the number of columns in a record!=the number of cloumns:" + " data has " + columns.length + ", expected " + m_address.getColumnNameArray().length); return false; } /* build map <columname, value> i.e. * {<c1, v1>, <c2, v2>...} for a data record */ for (int i=0; i< columns.length; i++) { String key = m_address.getColumnNameArray()[i]; Class xlass = (Class) (m_address.getColumnNameToTypeMap().get(key)); //Object xlass = Class.forName((String) (m_address.getColumnNameToTypeMap().get(key))); /****TODO ****/ if (xlass.isInstance(longObj)) { long value; try { value = Long.parseLong(columns[i]); } catch (NumberFormatException e) { LOGGER.info( "incorrect long value=" + columns[i]); return false; } event.put(m_address.getColumnNameArray()[i], value); } else if (xlass.isInstance(intObj)) { int value; try { value = Integer.parseInt(columns[i]); } catch (NumberFormatException e) { LOGGER.info( "incorrect int value=" + columns[i]); return false; } event.put(m_address.getColumnNameArray()[i], value); }else if (xlass.isInstance(floatObj)) { float value; try { value = Float.parseFloat(columns[i]); }catch (NumberFormatException e) { LOGGER.info( "incorrect float value=" + columns[i]); return false; } event.put(m_address.getColumnNameArray()[i], value); } else { event.put(m_address.getColumnNameArray()[i], columns[i]); } } return true; } public boolean isEOF() { return m_eof; } }