All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ebay.jetstream.event.channel.file.InboundFileChannel Maven / Gradle / Ivy

/*******************************************************************************
 *  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.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import com.ebay.jetstream.common.ShutDownable;
import com.ebay.jetstream.config.ContextBeanChangedEvent;
import com.ebay.jetstream.counter.LongCounter;
import com.ebay.jetstream.counter.LongEWMACounter;
import com.ebay.jetstream.event.EventException;
import com.ebay.jetstream.event.JetstreamEvent;
import com.ebay.jetstream.event.JetstreamReservedKeys;
import com.ebay.jetstream.event.RetryEventCode;
import com.ebay.jetstream.event.advice.Advice;
import com.ebay.jetstream.event.channel.InboundChannel;
import com.ebay.jetstream.event.support.AbstractEventSource;
import com.ebay.jetstream.management.Management;
import com.ebay.jetstream.messaging.MessageServiceTimer;
import com.ebay.jetstream.notification.AlertListener;
import com.ebay.jetstream.xmlser.XSerializable;

/***
 * specify this class in the subscriber's wiring file to get registered into Spring framework
 * @author gjin
 *
 */

/**
 * with this annotation will see http://127.0.0.1:1504/Event/Channel/InboundFileChannel
 * if the bean name is "InboundFileChannel" 
 */
@ManagedResource(objectName = "Event/Channel", description = "Inbound File component")
public class InboundFileChannel extends AbstractEventSource implements InboundChannel, BeanNameAware,
	               InitializingBean, ApplicationListener, XSerializable, ShutDownable {

	Logger logger = LoggerFactory.getLogger("com.ebay.jetstream.event.channel.file.InboundFileChannel");
	
	//private FileChannelAddress m_address;
	private InboundFileChannelHelper m_fcHelper;
  
	private Advice m_adviceListener;

	private final AtomicBoolean m_isPaused = new AtomicBoolean(false);

	private final LongCounter m_totalEventsReceived = new LongCounter();

	private final LongCounter m_pauseCount = new LongCounter();

	private final LongCounter m_resumeCount = new LongCounter();

	private final LongCounter m_totalEventsSent = new LongCounter();
  
	private final LongCounter m_totalEventsDropped = new LongCounter();

	private final LongCounter m_eventSentToAdviceListener = new LongCounter();
  
	private LongEWMACounter m_eventsReceivedPerSec;
	private LongEWMACounter m_eventsSentPerSec;

	private final AtomicBoolean m_shutdownStatus = new AtomicBoolean(false); // NOPMD
  
	private int m_numOfEventsPerSec;  /* the trunkSize: number of events/per sec */

	private PlayThread m_pt = new PlayThread();
	
	private AlertListener m_AlertListener;

	//private AtomicBoolean m_restartable = new AtomicBoolean(false);
  
	public InboundFileChannel() {
	}

	public void afterPropertiesSet() throws Exception {
   
	}

	public void setAlertListener(AlertListener al) {
		if (al == null) {
			logger.error( "AlertListener is null unexpectedly");
			return;
		}
		m_AlertListener = al;
	}
	/**
	 * clean the memory
	 * close the file
	 */
  	public void close() throws EventException {
  		logger.info( "Closing Inbound File Channel");
   		Management.removeBeanOrFolder(getBeanName(), this);
  		
  	    m_eventsReceivedPerSec.destroy();
  		m_eventsSentPerSec.destroy();
  	  
  		/* close the file */
  		m_fcHelper.closeForRead();
  		
  	}
  	
  	public void open() throws EventException {
  		//LOGGER.info( "Opening Inbound File Channel");
  		logger.info( "Openning Inbound File Channel");
  		/* put my name Event/FileChannel to the page */
  		Management.addBean(getBeanName(), this);
  	    m_eventsReceivedPerSec = new LongEWMACounter(60, MessageServiceTimer.sInstance().getTimer());
  		m_eventsSentPerSec = new LongEWMACounter(60, MessageServiceTimer.sInstance().getTimer());
  	  

  		/* open the event file, be ready for play*/
  		try {
  			m_fcHelper.openForRead();
  		} catch (EventException ee) {
  			//LOGGER.error( "InboundFileChannel open() get e=" + ee);
  			logger.error( "--InboundFileChannel open() get e=" + ee);
  	  		/*** instead of forcing application to exit, we choose to post an error status at dashboard */
			if (m_AlertListener != null) {
				m_AlertListener.sendAlert(getBeanName(),  "InboundFileChannel open() get e=" + ee, AlertListener.AlertStrength.RED);
			} else {
				throw ee;
			}
				
  		}


  	}
  	
  	private void fireEvent(JetstreamEvent evt) {
  		super.fireSendEvent(evt);
  	}

  	/* just for satisfying the interface */
  	public void flush() throws EventException {
  	}
 
  	@Override
  	public int getPendingEvents() {
  		if (m_fcHelper.isEOF()) {
  			return 0;
  		} else {
  			return 1; /* we do not know how many left */
  		}
  	}
  	
  	public FileChannelAddress getAddress() {
  		return m_fcHelper.getAddress();
  	}

  	public Advice getAdviceListener() {
  		return m_adviceListener;
  	}



  	/**
  	 * 
  	 * @return
  	 */
  	public long getEventSentToAdviceListener() {
  		return m_eventSentToAdviceListener.get();
  	}

  	/**
  	 * @return the messagesReceived
  	 */
  	public long getTotalEventsReceived() {
  		return m_totalEventsReceived.get();
  	}



  	/**
  	 * @return the pauseCount
  	 */
  	public long getTotalPauseCount() {
  		return m_pauseCount.get();
  	}




  	/**
  	 * @return the resumeCount
  	 */
  	public long getTotalResumeCount() {
  		return m_resumeCount.get();
  	}

  
  	public boolean getShutdownStatus() {
  		return m_shutdownStatus.get();
  	}

  	private void incrementAdviceListenerCount() {
  		m_eventSentToAdviceListener.increment();
  	}

  	private void incrementSentCount() {
  		m_totalEventsSent.increment();
  	}
  	
  	

  	@Override
  	public long getEventsReceivedPerSec() {
  		return m_eventsReceivedPerSec.get();
  	}

  	@Override
  	public long getEventsSentPerSec() {
  		return m_eventsSentPerSec.get();
  	} 

	@ManagedOperation
	public void startReplayWithEventFile() {
		Thread.State s = m_pt.getState();
		/* if it is newly created or terminated, then start it */
		if (s == Thread.State.NEW || s == Thread.State.TERMINATED) {
			m_pt.start();
		} else {
	  		logger.warn( "the replay thread was started with state=" + s);
		}
	}

	@ManagedOperation
	public void restart() {
		Thread.State s = m_pt.getState();
		/* if it is newly created or terminated, then start it */
		if (s == Thread.State.TERMINATED) {
	  		/* open the event file */
	  		m_fcHelper.openForRead();
	  		/* restart play from the begin of the event file */
	  		m_pt = new PlayThread();
			m_pt.start();
		} else {
			logger.warn( "the replay thread either never started or running: state=" + s);
		}
	}
  
  	@Override
  	@ManagedOperation
  	public void pause() throws EventException {
  		if (m_isPaused.get()) {
  			logger.error( "InboundFileChannel " + getBeanName()
  					+ " could not be paused. It is already in paused state", "InboundFileChannel" + ".pause()");
  			return;
  		}
  		m_isPaused.set(true);
  		
  		logger.error( "Inbound File Channel paused.");

  	}
  	@Override
  	@ManagedOperation
  	public void resume() throws EventException {
  		if (!m_isPaused.get()) {
  			logger.error( "InboundFileChannel " + getBeanName()
  					+ " could not be resumed. It is already in resume state", "InboundFileChannel" + ".pause()");
  			return;
  		}

  		// if we are in paused state then first empty the pause Cache which could have received events
  		// that were in the pipeline when we unsubscribed in response to a pause.
  		if (m_isPaused.get() && !m_shutdownStatus.get()) {

  			m_isPaused.set(false);
  			if (m_resumeCount.addAndGet(1) < 0) {
  				m_resumeCount.set(0);
  			}
  			
  			/* resume by interrupt the thread from "sleep()" */
  			m_pt.interrupt();
  		}

  	}

  	@Override
  	protected void processApplicationEvent(ApplicationEvent event) {
  		if (event instanceof ContextBeanChangedEvent) {
  			ContextBeanChangedEvent bcInfo = (ContextBeanChangedEvent) event;

  			//Calculate changes
  			if (bcInfo.isChangedBean(getAddress())) {
  				logger.info( "Received new configuration for InboundMessageChannel - " + getBeanName());
  				try {
  					close();
  				}
  				catch (Throwable e) {
  					//swallow the exception and log
  					String errMsg = new String("Error closing InboundMessagingChannel while applying new config - ");
  					errMsg += e.getMessage();

  					logger.error(  errMsg);

  				}
  				setAddress((FileChannelAddress) bcInfo.getChangedBean());
  				try {
  					open();
  				}
  				catch (Throwable e) {
  					// swallow the exception and log
  					String errMsg = new String("Error opening InboundMessagingChannel while applying new config - ");
  					errMsg += e.getMessage();

  					logger.error(  errMsg);

  				}
  			}
  		}

  	}

  	/**
  	 * @param messagesReceived
  	 *          the messagesReceived to set
  	 */
  	@ManagedOperation
  	public void resetEventsReceived() {
  		m_totalEventsReceived.set(0);
  	}

  	/**
  	 * @param pauseCount
  	 *          the pauseCount to set
  	 */	
  	@ManagedOperation
  	public void resetPauseCount() {
  		m_pauseCount.set(0);
  	}

  	/**
  	 * @param resumeCount
  	 *          the resumeCount to set
  	 */
  	@ManagedOperation
  	public void resetResumeCount() {
  		m_resumeCount.set(0);
  	}

  	/**
  	 * 
  	 */
  	@ManagedOperation
  	public void resetStats() {
  		m_totalEventsReceived.reset();
   		m_pauseCount.reset();
  		m_resumeCount.reset();
  		m_totalEventsSent.reset();
   		m_totalEventsDropped.reset();
   		
   	    m_totalEventsReceived.reset();
   	    m_eventsReceivedPerSec.reset();
  	}



  	private void sendToAdviceListener(JetstreamEvent event, RetryEventCode code, String msg) {
  		if (m_adviceListener != null) {
		  	m_adviceListener.retry(event, code, msg);
      		incrementAdviceListenerCount();
    	}
  	}

  	/**
  	 * @param address
  	 */
  	public void setAddress(FileChannelAddress address) {
	  	//m_address = (FileChannelAddress) address;
	  	m_fcHelper = new InboundFileChannelHelper(address);
  	}

  	public void setAdviceListener(Advice adviceListener) {
	  	m_adviceListener = adviceListener;
  	}

  
  	@Override
  	public void shutDown() {
  		m_shutdownStatus.set(true);
  		close();
  	}


  	@Override
  	public long getTotalEventsSent() {
  		return m_totalEventsSent.get();
  	}

  	@Override
	public long getTotalEventsDropped() {
		return m_totalEventsDropped.get();
	}





	public int getNumOfEventPerSec() {
		return m_numOfEventsPerSec;
	}
	public void setNumOfEventPerSec(int rate) {
		m_numOfEventsPerSec = rate;
	}
	
	

	class PlayThread extends Thread {
		Map eventMap;
		public void run() {
			//System.out.println("play thread starts to run: eof=" + m_fcHelper.isEOF() + ", paused=" + m_isPaused.get()); //KEEPME
			while (!m_fcHelper.isEOF()) {
				while (m_isPaused.get()) {
					try {
						Thread.sleep(1000*10);
					} catch (InterruptedException ie) {
						System.out.println("##### innterrupted "); //KEEPME
					}
				}
				long startTimeMS = System.currentTimeMillis();
				
				int count;
				//System.out.println("##### m_numOfEventPerSec=" + m_numOfEventsPerSec); //KEEPME
				for (count = 0; count < m_numOfEventsPerSec; count++) {
					//System.out.println("##### getNextEventMap()");
					eventMap = m_fcHelper.getNextEventMap();
 
					if (eventMap == null) {
						break;
					}
					JetstreamEvent event = extractEventFromMap(eventMap);
					if (event != null) {
						m_totalEventsReceived.increment();
						m_eventsReceivedPerSec.increment();
						fireSendEvent(event);
					}
				}
				
				long elapsedTimeInMS = System.currentTimeMillis() - startTimeMS;
				
				/* if less than one sec */
				if (elapsedTimeInMS < 1000) {

					try {
						//System.out.println("##### sleep " + (1000 - elapsedTimeInMS) + " millisec"); //KEEPME
						TimeUnit.MILLISECONDS.sleep(1000 - elapsedTimeInMS);
					} catch (InterruptedException e) {
						// swallow
					}
				}

			}
			/* end of file. close it for restart */
			m_fcHelper.closeForRead();
		}
		private JetstreamEvent extractEventFromMap(Map eventMap) {
			JetstreamEvent event;
			if (eventMap == null) {
				return null;
			}
			
			
			String type = (String) eventMap.get(JetstreamReservedKeys.EventType.toString());
			
			eventMap.remove(JetstreamReservedKeys.EventId.toString());
			eventMap.remove(JetstreamReservedKeys.EventType.toString());
			
			/* TODO: it appears we do not have any class implementing EventId 
			 * so we put null for the time being
			 */
			event = new JetstreamEvent();
			if (type != null) {
				event.setEventType(type);
			}
			
			for (Map.Entry entry: eventMap.entrySet()) {
				event.put(entry.getKey(), entry.getValue());
			}
			return event;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy