/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.databridge.core.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.databridge.commons.Attribute;
import org.wso2.carbon.databridge.commons.StreamDefinition;
import org.wso2.carbon.databridge.commons.exception.DifferentStreamDefinitionAlreadyDefinedException;
import org.wso2.carbon.databridge.commons.exception.MalformedStreamDefinitionException;
import org.wso2.carbon.databridge.commons.utils.DataBridgeCommonsUtils;
import org.wso2.carbon.databridge.commons.utils.EventDefinitionConverterUtils;
import org.wso2.carbon.databridge.core.*;
import org.wso2.carbon.databridge.core.Utils.AgentSession;
import org.wso2.carbon.databridge.core.Utils.EventComposite;
import org.wso2.carbon.databridge.core.conf.DataBridgeConfiguration;
import org.wso2.carbon.databridge.core.definitionstore.AbstractStreamDefinitionStore;
import org.wso2.carbon.databridge.core.definitionstore.StreamAddRemoveListener;
import org.wso2.carbon.databridge.core.exception.StreamDefinitionStoreException;
import org.wso2.carbon.databridge.core.internal.authentication.AuthenticationHandler;
import org.wso2.carbon.databridge.core.internal.queue.EventQueue;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Dispactches events and their definitions subscribers
*/
public class EventDispatcher {
private List<AgentCallback> subscribers = new ArrayList<AgentCallback>();
private List<RawDataAgentCallback> rawDataSubscribers = new ArrayList<RawDataAgentCallback>();
private AbstractStreamDefinitionStore streamDefinitionStore;
private Map<Integer, StreamTypeHolder> domainNameStreamTypeHolderCache = new ConcurrentHashMap<Integer, StreamTypeHolder>();
private EventQueue eventQueue;
private AuthenticationHandler authenticationHandler;
private static final Log log = LogFactory.getLog(EventDispatcher.class);
public EventDispatcher(AbstractStreamDefinitionStore streamDefinitionStore,
DataBridgeConfiguration dataBridgeConfiguration, AuthenticationHandler authenticationHandler) {
this.eventQueue = new EventQueue(subscribers, rawDataSubscribers, dataBridgeConfiguration);
this.streamDefinitionStore = streamDefinitionStore;
this.authenticationHandler = authenticationHandler;
streamDefinitionStore.subscribe(new StreamAddRemoveListener() {
@Override
public void streamAdded(int tenantId, String streamId) {
}
@Override
public void streamRemoved(int tenantId, String streamId) {
removeStreamDefinitionFromStreamTypeHolder(tenantId, streamId);
}
});
}
public void addCallback(AgentCallback agentCallback) {
subscribers.add(agentCallback);
}
/**
* Add thrift subscribers
*
* @param agentCallback
*/
public void addCallback(RawDataAgentCallback agentCallback) {
rawDataSubscribers.add(agentCallback);
}
public synchronized String defineStream(String streamDefinition, AgentSession agentSession)
throws MalformedStreamDefinitionException,
DifferentStreamDefinitionAlreadyDefinedException,
StreamDefinitionStoreException {
int tenantId = agentSession.getCredentials().getTenantId();
StreamDefinition newStreamDefinition = EventDefinitionConverterUtils.convertFromJson(streamDefinition);
StreamTypeHolder streamTypeHolder = getStreamDefinitionHolder(tenantId);
StreamAttributeComposite attributeComposite = streamTypeHolder.getAttributeComposite(newStreamDefinition.getStreamId());
if (attributeComposite != null) {
StreamDefinition existingStreamDefinition = attributeComposite.getStreamDefinition();
if (!existingStreamDefinition.equals(newStreamDefinition)) {
throw new DifferentStreamDefinitionAlreadyDefinedException("Similar event stream for " + newStreamDefinition + " with the same name and version already exist: " + streamDefinitionStore.getStreamDefinition(newStreamDefinition.getName(), newStreamDefinition.getVersion(), tenantId));
}
newStreamDefinition = existingStreamDefinition;
} else {
for (StreamAttributeComposite aAttributeComposite : streamTypeHolder.getAttributeCompositeMap().values()) {
validateStreamDefinition(newStreamDefinition, aAttributeComposite.getStreamDefinition());
}
updateDomainNameStreamTypeHolderCache(newStreamDefinition, tenantId);
streamDefinitionStore.saveStreamDefinition(newStreamDefinition, tenantId);
}
for (AgentCallback agentCallback : subscribers) {
agentCallback.definedStream(newStreamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.definedStream(newStreamDefinition, tenantId);
}
return newStreamDefinition.getStreamId();
}
public synchronized String defineStream(String streamDefinition, AgentSession agentSession,
String indexDefinition)
throws MalformedStreamDefinitionException,
DifferentStreamDefinitionAlreadyDefinedException,
StreamDefinitionStoreException {
int tenantId = agentSession.getCredentials().getTenantId();
StreamDefinition newStreamDefinition = EventDefinitionConverterUtils.convertFromJson(streamDefinition);
StreamTypeHolder streamTypeHolder = getStreamDefinitionHolder(tenantId);
StreamAttributeComposite attributeComposite = streamTypeHolder.getAttributeComposite(newStreamDefinition.getStreamId());
if (attributeComposite != null) {
StreamDefinition existingStreamDefinition = attributeComposite.getStreamDefinition();
if (!existingStreamDefinition.equals(newStreamDefinition)) {
throw new DifferentStreamDefinitionAlreadyDefinedException("Similar event stream for " + newStreamDefinition + " with the same name and version already exist: " + streamDefinitionStore.getStreamDefinition(newStreamDefinition.getName(), newStreamDefinition.getVersion(), tenantId));
}
newStreamDefinition = existingStreamDefinition;
} else {
for (StreamAttributeComposite aAttributeComposite : streamTypeHolder.getAttributeCompositeMap().values()) {
validateStreamDefinition(newStreamDefinition, aAttributeComposite.getStreamDefinition());
}
updateDomainNameStreamTypeHolderCache(newStreamDefinition, tenantId);
streamDefinitionStore.saveStreamDefinition(newStreamDefinition, tenantId);
}
newStreamDefinition.createIndexDefinition(indexDefinition);
for (AgentCallback agentCallback : subscribers) {
agentCallback.definedStream(newStreamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.definedStream(newStreamDefinition, tenantId);
}
return newStreamDefinition.getStreamId();
}
private void validateStreamDefinition(StreamDefinition newStreamDefinition,
StreamDefinition existingStreamDefinition)
throws DifferentStreamDefinitionAlreadyDefinedException {
if (newStreamDefinition.getName().equals(existingStreamDefinition.getName())) {
validateAttributes(newStreamDefinition.getMetaData(), existingStreamDefinition.getMetaData(), "meta", newStreamDefinition, existingStreamDefinition);
validateAttributes(newStreamDefinition.getCorrelationData(), existingStreamDefinition.getCorrelationData(), "correlation", newStreamDefinition, existingStreamDefinition);
validateAttributes(newStreamDefinition.getPayloadData(), existingStreamDefinition.getPayloadData(), "payload", newStreamDefinition, existingStreamDefinition);
}
}
private void validateAttributes(List<Attribute> newAttributes,
List<Attribute> existingAttributes, String type,
StreamDefinition newStreamDefinition,
StreamDefinition existingStreamDefinition)
throws DifferentStreamDefinitionAlreadyDefinedException {
if (newAttributes != null && existingAttributes != null) {
for (Attribute attribute : newAttributes) {
for (Attribute existingAttribute : existingAttributes) {
if (attribute.getName().equals(existingAttribute.getName())) {
if (attribute.getType() != existingAttribute.getType()) {
throw new DifferentStreamDefinitionAlreadyDefinedException("Attribute type mismatch " + type + " " +
attribute.getName() + " type:" + attribute.getType() +
" was already defined with type:" + existingAttribute.getType() +
" in " + existingStreamDefinition + ", hence " + newStreamDefinition +
" cannot be defined");
}
}
}
}
}
}
public void publish(Object eventBundle, AgentSession agentSession,
EventConverter eventConverter) {
eventQueue.publish(new EventComposite(eventBundle, getStreamDefinitionHolder(agentSession.getCredentials().getTenantId()), agentSession, eventConverter));
}
private StreamTypeHolder getStreamDefinitionHolder(int tenantId) {
// this will occur only outside of carbon (ex: Siddhi)
StreamTypeHolder streamTypeHolder = domainNameStreamTypeHolderCache.get(tenantId);
if (streamTypeHolder != null) {
if (log.isDebugEnabled()) {
String logMsg = "Event stream holder for tenant : " + tenantId + " : \n ";
logMsg += "Meta, Correlation & Payload Data Type Map : ";
for (Map.Entry entry : streamTypeHolder.getAttributeCompositeMap().entrySet()) {
logMsg += "StreamID=" + entry.getKey() + " : ";
logMsg += "Meta= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[0]) + " : ";
logMsg += "Correlation= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[1]) + " : ";
logMsg += "Payload= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[2]) + "\n";
}
log.debug(logMsg);
}
return streamTypeHolder;
} else {
return initDomainNameStreamTypeHolderCache(tenantId);
}
}
public synchronized void updateStreamDefinitionHolder(AgentSession agentSession) {
int tenantId = agentSession.getCredentials().getTenantId();
StreamTypeHolder streamTypeHolder = domainNameStreamTypeHolderCache.get(tenantId);
if (streamTypeHolder != null) {
if (log.isDebugEnabled()) {
String logMsg = "Event stream holder for tenant : " + tenantId + " : \n ";
logMsg += "Meta, Correlation & Payload Data Type Map : ";
for (Map.Entry entry : streamTypeHolder.getAttributeCompositeMap().entrySet()) {
logMsg += "StreamID=" + entry.getKey() + " : ";
logMsg += "Meta= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[0]) + " : ";
logMsg += "Correlation= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[1]) + " : ";
logMsg += "Payload= " + Arrays.deepToString(((StreamAttributeComposite) entry.getValue()).getAttributeTypes()[2]) + "\n";
}
log.debug(logMsg);
}
updateDomainNameStreamTypeHolderCache(tenantId);
}
}
private synchronized void updateDomainNameStreamTypeHolderCache(
StreamDefinition streamDefinition, int tenantId) {
StreamTypeHolder streamTypeHolder = getStreamDefinitionHolder(tenantId);
streamTypeHolder.putStreamDefinition(streamDefinition);
}
public synchronized void reloadDomainNameStreamTypeHolderCache(int tenantId){
StreamTypeHolder streamTypeHolder = getStreamDefinitionHolder(tenantId);
Collection<StreamDefinition> allStreamDefinitions =
streamDefinitionStore.getAllStreamDefinitions(tenantId);
for (StreamDefinition streamDefinition: allStreamDefinitions){
if (!streamTypeHolder.getAttributeCompositeMap().containsKey(streamDefinition.getStreamId())){
streamTypeHolder.putStreamDefinition(streamDefinition);
for (AgentCallback agentCallback : subscribers) {
agentCallback.definedStream(streamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.definedStream(streamDefinition, tenantId);
}
}
}
}
private synchronized StreamTypeHolder initDomainNameStreamTypeHolderCache(int tenantId) {
StreamTypeHolder streamTypeHolder = domainNameStreamTypeHolderCache.get(tenantId);
if (null == streamTypeHolder) {
streamTypeHolder = new StreamTypeHolder(tenantId);
streamTypeHolder.setEventDispatcherCallback(this);
Collection<StreamDefinition> allStreamDefinitions =
streamDefinitionStore.getAllStreamDefinitions(tenantId);
if (null != allStreamDefinitions) {
for (StreamDefinition aStreamDefinition : allStreamDefinitions) {
streamTypeHolder.putStreamDefinition(aStreamDefinition);
for (AgentCallback agentCallback : subscribers) {
agentCallback.definedStream(aStreamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.definedStream(aStreamDefinition, tenantId);
}
}
}
domainNameStreamTypeHolderCache.put(tenantId, streamTypeHolder);
}
return streamTypeHolder;
}
private synchronized StreamTypeHolder updateDomainNameStreamTypeHolderCache(int tenantId) {
StreamTypeHolder streamTypeHolder = domainNameStreamTypeHolderCache.get(tenantId);
if (null != streamTypeHolder) {
Collection<StreamDefinition> allStreamDefinitions =
streamDefinitionStore.getAllStreamDefinitions(tenantId);
if (null != allStreamDefinitions) {
for (StreamDefinition aStreamDefinition : allStreamDefinitions) {
if (streamTypeHolder.getAttributeComposite(aStreamDefinition.getStreamId()) == null) {
streamTypeHolder.putStreamDefinition(aStreamDefinition);
for (AgentCallback agentCallback : subscribers) {
agentCallback.definedStream(aStreamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.definedStream(aStreamDefinition, tenantId);
}
}
}
List<String> streamIdList = new ArrayList<String>();
for (StreamDefinition streamDefinition : allStreamDefinitions) {
streamIdList.add(streamDefinition.getStreamId());
}
Iterator<String> streamIdIterator = streamTypeHolder.getAttributeCompositeMap().keySet().iterator();
while (streamIdIterator.hasNext()) {
if (!streamIdList.contains(streamIdIterator.next())) {
streamIdIterator.remove();
}
}
}
domainNameStreamTypeHolderCache.put(tenantId, streamTypeHolder);
}
return streamTypeHolder;
}
public List<AgentCallback> getSubscribers() {
return subscribers;
}
public List<RawDataAgentCallback> getRawDataSubscribers() {
return rawDataSubscribers;
}
public String findStreamId(String streamName, String streamVersion, AgentSession agentSession)
throws StreamDefinitionStoreException {
int tenantId = agentSession.getCredentials().getTenantId();
//Updating the cache when calling the findStreamId to keep the sync between the stream manager and register with data publisher
//for CEP - need to review and fix
updateDomainNameStreamTypeHolderCache(tenantId);
StreamTypeHolder streamTypeHolder = getStreamDefinitionHolder(tenantId);
StreamAttributeComposite attributeComposite = streamTypeHolder.getAttributeComposite(DataBridgeCommonsUtils.generateStreamId(streamName, streamVersion));
if (attributeComposite != null) {
return attributeComposite.getStreamDefinition().getStreamId();
}
return null;
}
public boolean deleteStream(String streamName, String streamVersion,
AgentSession agentSession) {
int tenantId = agentSession.getCredentials().getTenantId();
String streamId = DataBridgeCommonsUtils.generateStreamId(streamName, streamVersion);
StreamDefinition streamDefinition = removeStreamDefinitionFromStreamTypeHolder(tenantId, streamId);
if (streamDefinition != null) {
for (AgentCallback agentCallback : subscribers) {
agentCallback.removeStream(streamDefinition, tenantId);
}
for (RawDataAgentCallback agentCallback : rawDataSubscribers) {
agentCallback.removeStream(streamDefinition, tenantId);
}
}
return streamDefinitionStore.deleteStreamDefinition(streamName, streamVersion, tenantId);
}
private synchronized StreamDefinition removeStreamDefinitionFromStreamTypeHolder(int tenantId,
String streamId) {
StreamTypeHolder streamTypeHolder = domainNameStreamTypeHolderCache.get(tenantId);
if (streamTypeHolder != null) {
StreamAttributeComposite attributeComposite = streamTypeHolder.getAttributeCompositeMap().remove(streamId);
if (attributeComposite != null) {
return attributeComposite.getStreamDefinition();
}
}
return null;
}
}