/******************************************************************************* * 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; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.NamedBean; import com.ebay.jetstream.util.CommonUtils; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoSerializable; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** * JetstreamEvent is a generalized event structure for wrapping underlying event implementations. The JetstreamEvent type is * used throughout the event framework for processing events. There are few distinguished attributes of the event, * primarily required for routing and state management. The remaining contents of the events are opaque, with the * semantics being defined by the query processing statements. <br> * <br> * The attributes of the events are retrieved via the Map.get method. The attributes specified by EQL are keys directly * into the Map.get method. The key must be a string while the type of the value can vary depending upon the context. * EQL will attempt to make the appropriate type conversions. * * Metadata can be passed in this event between various stages within the process. The metadata will not be serialized over the wire. * * @author shmurthy@ebay.com - derived from original impl created by Dan Pritchett * */ public class JetstreamEvent implements Map<String, Object>, Externalizable, Cloneable, KryoSerializable { private static final long serialVersionUID = -2339580269438201272L; private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.event"); private String[] m_topics; private String[] m_urls; private final Map<String, Object> m_backingMap; private final Map<String, Object> m_metadata = new HashMap<String, Object>(2); public JetstreamEvent() { this(new HashMap<String, Object>()); } public JetstreamEvent(Map<String, Object> map) { this(null, null, map); } public JetstreamEvent(String eventType, EventId id, Map<String, Object> map) { m_backingMap = map != null ? map : new HashMap<String, Object>(); if (eventType != null) setEventType(eventType); if (id != null) setEventId(id); } public void addMetaData(String key, Object value) { m_metadata.put(key, value); } public Object getMetaData(String key) { return m_metadata.get(key); } @Override public void clear() { m_backingMap.clear(); } @Override public boolean containsKey(Object key) { return m_backingMap.containsKey(key); } @Override public boolean containsValue(Object value) { return m_backingMap.containsValue(value); } @Override public Set<java.util.Map.Entry<String, Object>> entrySet() { return m_backingMap.entrySet(); } @Override public boolean equals(Object o) { return super.equals(o) || m_backingMap.equals(o); } @Override public Object get(Object key) { return m_backingMap.get(key); } /** * The event id is an opaque handle to the event. This is used for state tracking in the ESP system. * * @return the opaque event identifier */ public EventId getEventId() { return (EventId) get(JetstreamReservedKeys.EventId.toString()); } /** * All events must be defined by a type. The ESP system considers an event to be uniquely identified by the * combination of type and identifier. Types are also used in some cases for routing events. */ public String getEventType() { return (String) get(JetstreamReservedKeys.EventType.toString()); } /** * @return A copy of this event with any entries held with a reserved key stripped */ public JetstreamEvent getFilteredEvent() { JetstreamEvent newEvent = new JetstreamEvent(); for (Map.Entry<String, Object> entry : m_backingMap.entrySet()) { if (JetstreamReservedKeys.isReserved(entry.getKey())) continue; newEvent.put(entry.getKey(), entry.getValue()); } return newEvent; } public String[] getForwardingTopics() { return m_topics; } public String[] getForwardingUrls() { return m_urls; } @Override public int hashCode() { return m_backingMap.hashCode(); } @Override public boolean isEmpty() { return m_backingMap.isEmpty(); } @Override public Set<String> keySet() { return m_backingMap.keySet(); } /** * Any caller is required to pass a source Identifier which will be part of the log message. The * implementation of this method must add the event ID to the log message along with the passed source Identifier * * @param source * @return */ public void log(NamedBean source) { LOGGER.debug( "eventSource=" + source.getBeanName() + "&eventId = " + getEventId()); } @Override public Object put(String key, Object value) { return m_backingMap.put(key, value); } @Override public void putAll(Map<? extends String, ? extends Object> t) { m_backingMap.putAll(t); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int mapSize = in.readInt(); for (int i = 0; i < mapSize; i++) m_backingMap.put((String)in.readObject(), (Serializable) in.readObject()); } @Override public Object remove(Object key) { return m_backingMap.remove(key); } public void setEventId(EventId id) { put(JetstreamReservedKeys.EventId.toString(), id); } public void setEventType(String type) { put(JetstreamReservedKeys.EventType.toString(), type); } public void setForwardingTopics(String[] topics) { m_topics = topics; } public void setForwardingUrls(String[] urls) { m_urls = urls; } @Override public int size() { return m_backingMap.size(); } @Override public String toString() { return m_backingMap.toString(); } @Override public Collection<Object> values() { return m_backingMap.values(); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(m_backingMap.size()); Iterator<Entry<String, Object>> itr = m_backingMap.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, Object> entry1 = itr.next(); out.writeObject(entry1.getKey()); out.writeObject(entry1.getValue()); } } public JetstreamEvent clone() throws CloneNotSupportedException { JetstreamEvent event; try { event = CommonUtils.getDeepCopy(this); } catch (InstantiationException e) { return null; } catch (IllegalAccessException e) { return null; } event.m_metadata.putAll(this.m_metadata); if (this.m_urls != null) { event.m_urls = new String[this.m_urls.length]; System.arraycopy(this.m_urls, 0, event.m_urls, 0, this.m_urls.length); } if (this.m_topics != null) { event.m_topics = new String[this.m_topics.length]; System.arraycopy(this.m_topics, 0, event.m_topics, 0, this.m_topics.length); } return event; } @Override public void write(Kryo kryo, Output out) { out.writeInt(m_backingMap.size(), true); Iterator<Entry<String, Object>> itr = m_backingMap.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, Object> entry1 = itr.next(); out.writeString(entry1.getKey()); kryo.writeClassAndObject(out, entry1.getValue()); } } @Override public void read(Kryo kryo, Input in) { int mapSize = in.readInt(true); for (int i = 0; i < mapSize; i++) { m_backingMap.put(in.readString(), kryo.readClassAndObject(in)); } } }