/*******************************************************************************
* 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.support;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NamedBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.jmx.export.annotation.ManagedOperation;
import com.ebay.jetstream.config.AbstractNamedBean;
import com.ebay.jetstream.config.ContextBeanChangedEvent;
import com.ebay.jetstream.counter.LongCounter;
import com.ebay.jetstream.event.EventException;
import com.ebay.jetstream.event.EventSink;
import com.ebay.jetstream.event.EventSinkList;
import com.ebay.jetstream.event.EventSource;
import com.ebay.jetstream.event.JetstreamErrorCodes;
import com.ebay.jetstream.event.JetstreamEvent;
import com.ebay.jetstream.event.support.channel.PauseEvent;
import com.ebay.jetstream.event.support.channel.ResumeEvent;
/**
* @author shmurthy, xiaojuwu1
*/
public abstract class AbstractEventSource extends AbstractNamedBean implements
EventSource, ApplicationListener, ErrorTracker {
private boolean m_pauseIfAnyEventSinkPauses = true;
private final AtomicBoolean m_isSourcePaused = new AtomicBoolean(false);
private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.event.support");
private List<EventSink> m_pausedEventSink = new CopyOnWriteArrayList<EventSink>();
private EventSinkList m_sinkList = new EventSinkList();
private ErrorManager m_errors = new ErrorManager();
private final LongCounter m_cloneFailedCounter = new LongCounter();
protected void fireSendEvent(JetstreamEvent event) throws EventException {
if (LOGGER.isDebugEnabled())
event.log(this);
if (m_sinkList.getSinks().size() <= 0
&& getPausedEventSink().size() > 0) {
throw new EventException("All Event Sinks are paused.....",
JetstreamErrorCodes.EVENT_SINKS_PAUSED.toString());
}
int index = 0;
int lastIndex = m_sinkList.size() - 1;
for (EventSink sink : m_sinkList.getSinks()) {
// if we have more than 1 sink we need to clone the event to pass to
// another sink so they can
// independently modify the event concurrently
JetstreamEvent fwdEvent = null;
if (index < lastIndex) {
try {
fwdEvent = event.clone();
if (fwdEvent == null)
m_cloneFailedCounter.increment();
} catch (CloneNotSupportedException e) {
m_cloneFailedCounter.increment();
LOGGER.error(
"unable to clone event : "
+ e.getLocalizedMessage());
}
} else {
fwdEvent = event;
}
index++;
try {
if (fwdEvent != null)
sink.sendEvent(fwdEvent);
} catch (EventException e) {
if (m_pauseIfAnyEventSinkPauses) {
throw e;
}
}
}
}
@Override
public void addEventSink(EventSink sink) {
m_sinkList.addSink(sink);
}
@Override
public Collection<EventSink> getEventSinks() {
return m_sinkList;
}
@Override
public void removeEventSink(EventSink sink) {
m_sinkList.remove(sink);
}
@Override
public void setEventSinks(Collection<EventSink> sinks) {
if (sinks instanceof EventSinkList) {
m_sinkList = (EventSinkList) sinks;
} else {
for (EventSink s : sinks) {
addEventSink(s);
}
}
}
/**
* @return the pausedEventSink
*/
public List<EventSink> getPausedEventSink() {
return Collections.unmodifiableList(m_pausedEventSink);
}
private boolean isEventFromTheSink(List<EventSink> eventSinks,
String beanName) {
for (EventSink sink : eventSinks) {
if (beanName.equalsIgnoreCase(sink.getBeanName())) {
return true;
}
}
return false;
}
public boolean isPauseIfAnyEventSinkPauses() {
return m_pauseIfAnyEventSinkPauses;
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof PauseEvent) {
processPauseEvent((PauseEvent) event);
} else if (event instanceof ResumeEvent) {
processResumeEvent((ResumeEvent) event);
} else {
applyConfig(event);
processApplicationEvent(event);
}
}
private void applyConfig(ApplicationEvent event) {
if (event instanceof ContextBeanChangedEvent) {
ContextBeanChangedEvent bcInfo = (ContextBeanChangedEvent) event;
if (bcInfo.isChangedBean((EventSinkList) getEventSinks())) {
setEventSinks(((Collection<EventSink>) ((bcInfo
.getApplicationContext()).getBean(bcInfo.getBeanName()))));
}
}
}
public abstract void pause();
protected abstract void processApplicationEvent(ApplicationEvent event);
/**
*
* @param pauseEvent
*/
public void processPauseEvent(PauseEvent pauseEvent) {
Object eventSource = pauseEvent.getSource();
NamedBean bean = (NamedBean) eventSource;
Collection<EventSink> eventSinks = getEventSinks();
if (eventSinks != null) {
for (EventSink eventSink : eventSinks) {
if (bean.getBeanName()
.equalsIgnoreCase(eventSink.getBeanName())) {
removeEventSink(eventSink);
m_pausedEventSink.add(eventSink);
}
}
if (isEventFromTheSink(getPausedEventSink(), bean.getBeanName())
&& m_pauseIfAnyEventSinkPauses && !m_isSourcePaused.get()) {
m_isSourcePaused.set(true);
pause();
} else if (isEventFromTheSink(getPausedEventSink(),
bean.getBeanName())
&& getEventSinks().size() == 0 && !m_isSourcePaused.get()) {
m_isSourcePaused.set(true);
pause();
}
}
}
/**
*
* @param resumeEvent
*/
public void processResumeEvent(ResumeEvent resumeEvent) {
Object eventSource = resumeEvent.getSource();
NamedBean bean = (NamedBean) eventSource;
List<EventSink> eventSinks = new ArrayList<EventSink>();
eventSinks
.addAll(Collections.unmodifiableCollection(m_pausedEventSink));
if (eventSinks != null) {
for (EventSink eventSink : eventSinks) {
if (bean.getBeanName()
.equalsIgnoreCase(eventSink.getBeanName())) {
addEventSink(eventSink);
m_pausedEventSink.remove(eventSink);
}
}
if (isEventFromTheSink(eventSinks, bean.getBeanName())
&& m_pauseIfAnyEventSinkPauses
&& getPausedEventSink().size() == 0
&& m_isSourcePaused.get()) {
m_isSourcePaused.set(false);
resume();
} else if (isEventFromTheSink(eventSinks, bean.getBeanName())
&& !m_pauseIfAnyEventSinkPauses
&& getEventSinks().size() > 0 && m_isSourcePaused.get()) {
m_isSourcePaused.set(false);
resume();
}
}
}
@ManagedOperation
@Override
public void clearErrorList() {
m_errors.clearErrorList();
}
@Override
public String getErrors() {
return m_errors.toString();
}
@Override
public void registerError(Throwable t) {
m_errors.registerError(t);
}
@Override
public void registerError(Throwable t, JetstreamEvent evtCause) {
m_errors.registerError(t, evtCause);
}
public abstract void resume();
@Override
public void setErrorListMax(int nSize) {
m_errors.setErrorListMax(nSize);
}
/**
* @param pausedEventSink
* the pausedEventSink to set
*/
public void setPausedEventSink(List<EventSink> pausedEventSink) {
m_pausedEventSink = pausedEventSink;
}
public void setPauseIfAnyEventSinkPauses(boolean pauseIfAnyEventSinkPauses) {
m_pauseIfAnyEventSinkPauses = pauseIfAnyEventSinkPauses;
}
@Override
public String toString() {
return getBeanName();
}
}