
com.ebay.jetstream.event.processor.esper.EsperProcessor Maven / Gradle / Ivy
The newest version!
/*
Pulsar
Copyright (C) 2013-2015 eBay Software Foundation
Licensed under the GPL v2 license. See LICENSE for full terms.
*/
package com.ebay.jetstream.event.processor.esper;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationEvent;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import com.ebay.jestream.event.annotation.AnnotationConfiguration;
import com.ebay.jestream.event.annotation.StatementAnnotationInfo;
import com.ebay.jetstream.common.NameableThreadFactory;
import com.ebay.jetstream.config.ContextBeanChangedEvent;
import com.ebay.jetstream.counter.LongEWMACounter;
import com.ebay.jetstream.event.EventException;
import com.ebay.jetstream.event.EventSink;
import com.ebay.jetstream.event.EventSinkList;
import com.ebay.jetstream.event.JetstreamEvent;
import com.ebay.jetstream.event.RetryEventCode;
import com.ebay.jetstream.event.processor.AbstractQueuedEventProcessor;
import com.ebay.jetstream.event.processor.EventProcessRequest;
import com.ebay.jetstream.management.Management;
import com.ebay.jetstream.messaging.MessageServiceTimer;
import com.ebay.jetstream.notification.AlertListener;
import com.ebay.jetstream.notification.AlertListener.AlertStrength;
import com.ebay.jetstream.xmlser.Hidden;
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.soda.AnnotationPart;
import com.espertech.esper.client.soda.EPStatementObjectModel;
@ManagedResource(objectName = "Event/Processor", description = "Esper Processor")
public class EsperProcessor extends AbstractQueuedEventProcessor {
Logger logger = LoggerFactory.getLogger("com.ebay.jetstream.event.processor.esper.EsperProcessor");
private Random m_random = new SecureRandom();
private AtomicInteger m_unknownEventCount = new AtomicInteger();
private EPL m_epl;
private EsperEventListener m_esperEventListener;
private CopyOnWriteArrayList esperEngineHolder = new CopyOnWriteArrayList();
private final LongEWMACounter m_avgSendEventExeNanos = new LongEWMACounter(60, MessageServiceTimer.sInstance().getTimer());
private final Map m_recentEvents = new ConcurrentHashMap();
private final Map m_recentEventsFail = new ConcurrentHashMap();
private final Set m_setKnownEvent = new HashSet();
private EsperExceptionHandler m_esperExceptionHandler;
private String engineURI = null;
private ScheduledExecutorService m_watchDog = Executors.newScheduledThreadPool(1, new NameableThreadFactory("EsperWatchDog"));
private int m_checkIntervalInSeconds = 50 * 60;
@ManagedAttribute
public int getCheckIntervalInSeconds() {
return m_checkIntervalInSeconds;
}
public void setCheckIntervalInSeconds(int checkIntervalInSeconds) {
this.m_checkIntervalInSeconds = checkIntervalInSeconds;
}
@Override
public void afterPropertiesSet() throws Exception {
if (getAlertListener() != null) {
getAlertListener().sendAlert(this.getBeanName(), "INIT", AlertListener.AlertStrength.GREEN);
}
Management.addBean(getBeanName(), this);
for (EventSink eventSink : ((EventSinkList)getEventSinks()).getSinks()) {
if (!m_esperEventListener.getEventSinks().contains(eventSink))
m_esperEventListener.addEventSink(eventSink);
}
m_esperEventListener.setAdviceListener(getAdviceListener());
m_esperEventListener.setPropagateEventOnFailure(getConfiguration().isPropagateEventOnFailure());
m_esperEventListener.setAnnotationConfig(getConfiguration().getAnnotationConfigMap());
// check the exception status at the intervals if it's in exception, submit stop replay command;
m_watchDog.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
if (getEsperExceptionHandler() != null) {
if (getEsperExceptionHandler().IsInException()) {
getAdviceListener().stopReplay();
}
}
} catch (Exception ex) {
logger.error( ex.getLocalizedMessage() , ex);
}
}
}, 5, m_checkIntervalInSeconds, TimeUnit.SECONDS);
super.afterPropertiesSet();
/* if it is placed in the base class, then we may end up no update if the
* subclass does not report status. so leave it here
*/
if (getAlertListener() != null) {
getAlertListener().sendAlert(this.getBeanName(), "OK", AlertListener.AlertStrength.GREEN);
}
}
public long getAverageExeNanosToEsper() {
return m_avgSendEventExeNanos.get();
}
public String getRegisteredEvents() {
StringBuilder bldr = new StringBuilder();
for (String strEvent : m_setKnownEvent) {
if (bldr.length() > 0)
bldr.append(",");
bldr.append(strEvent);
}
return bldr.toString();
}
public EsperEventListener getEsperEventListener() {
return m_esperEventListener;
}
public int getUnknownEventCount() {
return m_unknownEventCount.get();
}
public Map getLastEventsSuccess() {
Map mapCopy = new HashMap();
for (String strEventType : m_recentEvents.keySet())
mapCopy.put(strEventType, m_recentEvents.get(strEventType).toString());
return mapCopy;
}
public void registerError(Throwable t, JetstreamEvent event) {
if (event != null)
m_recentEventsFail.put(event.getEventType(), event);
super.registerError(t, event);
}
public Map getLastEventsFailure() {
Map mapCopy = new HashMap();
for (String strEventType : m_recentEventsFail.keySet())
mapCopy.put(strEventType, m_recentEventsFail.get(strEventType).toString());
return mapCopy;
}
@Required
public void setEpl(EPL epl) {
m_epl = epl;
}
public void setEsperEventListener(EsperEventListener listener) {
m_esperEventListener = listener;
}
@Override
public void stop() {
super.stop();
esperEngineHolder.get(0).clear();
if (m_watchDog != null) {
m_watchDog.shutdownNow();
}
}
@Override
public void shutDown() {
pausePublisher("Application getting gracefulShutdown");
while (getQueuedEventCount() != 0) {
try {
Thread.sleep(100);
}catch (InterruptedException e) {
logger.error( e.getLocalizedMessage() , e);
}
}
//If the queue is empty, flush Esper context
esperEngineHolder.get(0).getEsperService().getEPRuntime().getEventSender("EsperEndEvent").sendEvent(new Object());
try {
Thread.sleep(100);
}catch (InterruptedException e) {
logger.error( e.getLocalizedMessage() , e);
}
stop(true);
esperEngineHolder.get(0).clear();
logger.warn( getBeanName() + " Shutdown has been completed");
if (m_watchDog != null) {
m_watchDog.shutdownNow();
}
}
@Override
protected String getComponentName() {
return "EsperProcessor";
}
@SuppressWarnings("unchecked")
@Hidden
@Override
protected EsperConfiguration getConfiguration() {
return super.getConfiguration();
}
@Override
protected EventProcessRequest getProcessEventRequest(JetstreamEvent event) {
return new ProcessEventRequest(getEsperEngine(), event, this);
}
@Override
protected void init() {
engineURI = getBeanName() + m_random.nextInt();
EsperInternals esper = new EsperInternals(engineURI);
boolean success = esper.init();
if (!success) {
if (getEsperExceptionHandler() != null) {
getEsperExceptionHandler().setExceptionStatus(true);
}
if(getAdviceListener() != null) {
getAdviceListener().stopReplay();
}
}
addEsperEngine(esper);
if (success) {
if(getAdviceListener() != null) {
getAdviceListener().startReplay();
}
}
}
protected boolean reinit() {
String newEgineURI = getBeanName() + m_random.nextInt();
//set new EngineURI to the processor. if init is not successful, will revert back to old engineURI
String oldEngineURI = engineURI;
engineURI = newEgineURI;
EsperInternals esper = new EsperInternals(newEgineURI);
boolean success = esper.init();
if (success) {
addEsperEngine(esper);
} else {
//reinit is not successful so reverting engineuri to old one.
engineURI = oldEngineURI;
esper.getEsperService().destroy();
esper.clear();
}
return success;
}
protected void fireSendEvent(JetstreamEvent event) {
// no-op, since it's the EsperListener that sends events to sinks
}
@Override
public void processApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextBeanChangedEvent) {
ContextBeanChangedEvent bcInfo = (ContextBeanChangedEvent)event;
if (bcInfo.isChangedBean((EventSinkList) getEventSinks())) {
//update sinklist to EsperEventListener.
m_esperEventListener.setEventSinks((EventSinkList)bcInfo.getChangedBean());
}
// update event definition bean
if(bcInfo.isChangedBean(getConfiguration().getDeclaredEvents())){
getConfiguration().setDeclaredEvents((EsperDeclaredEvents)bcInfo.getChangedBean());
// known events will be updated when EPL update happens
if(getAdviceListener() != null)
getAdviceListener().startReplay();
}
if (bcInfo.getBeanName().equals(getEpl().getBeanName())) {
EPL oldEPL = getEpl();
logger.info(
"Received dynamic notification to apply new EPLs");
try {
Object objChangeBean = bcInfo.getChangedBean();
EPL epl = (objChangeBean instanceof EPL) ? (EPL) objChangeBean
: getEpl();
setEpl(epl);
try {
if (reinit()) {
// resetting alert status
if (getEsperExceptionHandler() != null) {
getEsperExceptionHandler().clearAlertStatus();
}
// post advice to start replay
if (getAdviceListener() != null) {
getAdviceListener().startReplay();
}
queueEvent(new ProcessDestroyEngineRequest(
esperEngineHolder.remove(1)));
logger
.info(
"Received dynamic notification: Applied new EPLs");
Management.removeBeanOrFolder(oldEPL.getBeanName(), oldEPL);
Management.addBean(epl.getBeanName(), epl);
} else {
rollback(oldEPL, epl);
}
} catch (RuntimeException rte) {
rollback(oldEPL, epl);
}
} catch (Exception e) {
setEpl(oldEPL);
logger.error( e.getLocalizedMessage(), e);
}
}
}
super.processApplicationEvent(event);
}
private void rollback(EPL oldEPL, EPL epl) {
setEpl(oldEPL);
Management.removeBeanOrFolder(epl.getBeanName(), epl);
Management.addBean(oldEPL.getBeanName(), oldEPL);
logger
.error( "New EPL is not Working out...So reverting to OLD EPL");
/*** send out an alert and update the dashboard */
if (getAlertListener() != null) {
getAlertListener().sendAlert(this.getBeanName(), "New EPL is not Working out...So reverting to OLD EPL",
AlertListener.AlertStrength.RED);
}
}
/**
* Just to validate the given EPL.
* @param epl
* @return
* @throws EPException
*/
public boolean compileEPL(EPL epl) throws EPException {
EsperInternals esperInternals = null;
try {
if (epl != null) {
esperInternals = new EsperInternals("compileEngine", epl);
esperInternals.compile(false);
return true;
}
return false;
} finally {
if (esperInternals != null) {
esperInternals.getEsperService().destroy();
esperInternals.clear();
}
}
}
/**
* - When Exception reaches beyond threshold, don't submit event to queue. Drop the event.
*/
protected void queueEvent(JetstreamEvent event) throws EventException {
if (getEsperExceptionHandler() != null) {
if (getEsperExceptionHandler().IsInException()) {
if (!getEsperExceptionHandler().isAlertPosted()) {
postAlert(getEsperExceptionHandler().getLastException(),
AlertStrength.RED);
getEsperExceptionHandler().setAlertPostedStatus(true);
if (logger.isDebugEnabled())
logger
.debug(
"Esper Exception threshold reached...Sending Alert..");
// if we have an advice listener we will advice them to stop replay
// they will be adviced to start replay when new EPL is deployed
if(getAdviceListener() != null)
getAdviceListener().stopReplay();
}
if(getAdviceListener() != null){
getAdviceListener().retry(event, RetryEventCode.MSG_RETRY,getEsperExceptionHandler().getLastException());
}else{
//AdviseListener not available. no further submission to engine... drop it..
if (logger.isDebugEnabled())
logger.debug(
"Esper Exception threshold reached...Dropping events");
incrementEventDroppedCounter();
}
} else {
queueEventInternal(event);
}
} else {
queueEventInternal(event);
}
}
private void queueEventInternal(JetstreamEvent event){
if (m_setKnownEvent.contains(event.getEventType())){
super.queueEvent(event);
}
else {
m_recentEventsFail.put(event.getEventType(), event);
m_unknownEventCount.incrementAndGet();
}
}
public String getEngineURI() {
return engineURI;
}
private void addEsperEngine(EsperInternals esperInternals){
esperEngineHolder.add(0, esperInternals);
}
private EPL getEpl() {
return m_epl;
}
private EsperInternals getEsperEngine(){
return esperEngineHolder.get(0);
}
class EsperInternals {
private EPServiceProvider m_esperService;
private String m_strName;
private EPL m_suppliedEpl;
private EPAdministrator m_epAdmin;
EsperInternals(String strName) {
m_strName = strName;
}
EsperInternals(String strName, EPL epl) {
m_strName = strName;
m_suppliedEpl = epl;
}
public EPServiceProvider getEsperService() {
return m_esperService;
}
void clear() {
// nothing yet
}
void compile(boolean checkSink) throws EPException{
m_esperService = EPServiceProviderManager.getProvider(m_strName, getConfiguration().getEsperConfiguration());
m_epAdmin = m_esperService.getEPAdministrator(); // 6908 fix - getAdmin caused recursion here
for(Map.Entry annotConfig : getConfiguration().getAnnotationConfigMap().entrySet()){
m_epAdmin.getConfiguration().addImport(annotConfig.getValue().getClassName());
}
m_epAdmin.getConfiguration().addImport(com.ebay.jetstream.epl.EPLUtilities.class);
m_epAdmin.getConfiguration().addImport(com.ebay.jetstream.epl.EPLUtils.class);
m_epAdmin.getConfiguration().addPlugInSingleRowFunction("toJson", "com.ebay.jetstream.epl.EPLUtils", "toJsonString");
m_epAdmin.getConfiguration().addPlugInSingleRowFunction("fromJson", "com.ebay.jetstream.epl.EPLUtils", "fromJsonString");
m_epAdmin.getConfiguration().addPlugInSingleRowFunction("generateEventId", "com.ebay.jetstream.epl.EPLUtils", "generateEventId");
m_epAdmin.getConfiguration().addEventType("EsperStartup", Object.class);
m_epAdmin.getConfiguration().addEventType("EsperEndEvent", Object.class);
m_epAdmin.getConfiguration().addPlugInAggregationFunctionFactory("histogram", "com.ebay.jetstream.epl.HistogramAggregatorFactory");
m_epAdmin.getConfiguration().addPlugInAggregationFunctionFactory("distinctcount", "com.ebay.jetstream.event.processor.esper.aggregates.CardinalityAggregatorFactory");
m_epAdmin.getConfiguration().addPlugInAggregationFunctionFactory("percentile", "com.ebay.jetstream.event.processor.esper.aggregates.QuantileAggregatorFactory");
m_epAdmin.getConfiguration().addPlugInAggregationFunctionFactory("topN", "com.ebay.jetstream.event.processor.esper.aggregates.TopKAggregatorFactory");
m_suppliedEpl = m_suppliedEpl == null ? getEpl() : m_suppliedEpl;
for(String statement : m_suppliedEpl.getStatements()) {
EPStatementObjectModel model = m_epAdmin.compileEPL(statement);
List annots = model.getAnnotations();
if (getConfiguration().getAnnotationProcesssor() != null) {
Map> annotationPartsMapList = new HashMap>();
List parts = null;
for (AnnotationPart part : annots) {
parts = annotationPartsMapList.get(part.getName());
if (parts == null) {
parts = new LinkedList();
annotationPartsMapList.put(part.getName(),
parts);
}
parts.add(part);
}
EPStatement epStmt = null;
if (annotationPartsMapList.size() == 0) {
epStmt = m_epAdmin.create(model, null,
new StatementAnnotationInfo());
} else {
StatementAnnotationInfo annotationInfo;
try {
annotationInfo = getConfiguration()
.getAnnotationProcesssor()
.getStatementAnnotationInfo(
annots,
statement,
model,
annotationPartsMapList,
((EventSinkList) getEventSinks())
.getSinks());
} catch (Exception e) {
throw new EPException("Exception from Annotation processing.." , e);
}
epStmt = m_epAdmin
.create(model, null, annotationInfo);
if(m_esperEventListener != null)
epStmt.addListener(m_esperEventListener);
}
}
}
}
boolean init() {
try{
compile(true);
for (EventType type : m_epAdmin.getConfiguration().getEventTypes())
m_setKnownEvent.add(type.getName());
m_esperService.getEPRuntime().getEventSender("EsperStartup").sendEvent(new Object());
return true;
}
catch (Throwable e) {
if (getAlertListener() != null) {
getAlertListener().sendAlert(getBeanName(), " Esper Processor: init failed with e=" +e, AlertListener.AlertStrength.RED);
}
if (getEsperExceptionHandler() != null) {
getEsperExceptionHandler().setLastException(e.getMessage());
}
registerError(e);
e.printStackTrace(System.err);
logger.error( "Processor " + getBeanName()
+ " failed to prepare. The application will be shut down immediately. Exception: " + e.getMessage());
return false;
}
}
void processEvent(JetstreamEvent event) {
long lBeforeNanos = System.nanoTime();
String eventType = event.getEventType();
m_esperService.getEPRuntime().sendEvent(event, eventType);
m_avgSendEventExeNanos.add(System.nanoTime() - lBeforeNanos);
m_recentEvents.put(eventType, event);
}
}
@Override
public int getPendingEvents() {
return 0;
}
public EsperExceptionHandler getEsperExceptionHandler() {
return m_esperExceptionHandler;
}
public void setEsperExceptionHandler(
EsperExceptionHandler m_esperExceptionHandler) {
this.m_esperExceptionHandler = m_esperExceptionHandler;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy