/*
* Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.mediator.publishevent;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseException;
import org.apache.synapse.SynapseLog;
import org.apache.synapse.mediators.AbstractMediator;
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.MalformedStreamDefinitionException;
import org.wso2.carbon.databridge.commons.utils.DataBridgeCommonsUtils;
import org.wso2.carbon.event.sink.EventSink;
import org.wso2.carbon.event.sink.EventSinkService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Mediator that extracts data from current message payload/header according to the given configuration.
* Extracted information is sent as an event.
*/
public class PublishEventMediator extends AbstractMediator {
private static final String TASK_EXECUTING_TENANT_ID = "CURRENT_TASK_EXECUTING_TENANT_IDENTIFIER";
private EventSinkService eventSinkService = null;
private String streamName;
private String streamVersion;
private List<Property> metaProperties = new ArrayList<Property>();
private List<Property> correlationProperties = new ArrayList<Property>();
private List<Property> payloadProperties = new ArrayList<Property>();
private List<Property> arbitraryProperties = new ArrayList<Property>();
private EventSink eventSink;
private String eventSinkName;
@Override
public boolean isContentAware() {
return true;
}
/**
* This is called when a new message is received for mediation.
* Extracts data from message to construct an event based on the mediator configuration
* Sends the constructed event to the event sink specified in mediator configuration
*
* @param messageContext Message context of the message to be mediated
* @return Always returns true. (instructs to proceed with next mediator)
*/
@Override
public boolean mediate(MessageContext messageContext) {
if (messageContext.getEnvironment().isDebuggerEnabled()) {
if (super.divertMediationRoute(messageContext)) {
return true;
}
}
/*
Following will get the tenant-id if it's in the message context
This is useful when injecting the message via a Scheduled Task, etc. which uses threads that are not tenant aware
*/
Integer tenantId = null;
if (messageContext.getProperty(TASK_EXECUTING_TENANT_ID) != null && messageContext.getProperty(TASK_EXECUTING_TENANT_ID) instanceof Integer){
tenantId = (Integer) messageContext.getProperty(TASK_EXECUTING_TENANT_ID);
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId);
}
// first "getEventSink() == null" check is done to avoid synchronized(this) block each time mediate()
// gets called (to improve performance).
// second "getEventSink() == null" check inside synchronized(this) block is used to ensure only one thread
// sets event sink.
if (getEventSink() == null) {
synchronized (this) {
if (getEventSink() == null) {
try {
setEventSink(loadEventSink());
} catch (SynapseException e) {
String errorMsg = "Cannot mediate message. Failed to load event sink '" + getEventSinkName() +
"'. Error: " + e.getLocalizedMessage();
handleException(errorMsg, e, messageContext);
}
}
}
}
/*
Anything relates to tenant specific should be completed before this
*/
if(tenantId != null) {
PrivilegedCarbonContext.endTenantFlow();
}
SynapseLog synLog = getLog(messageContext);
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Start : " + PublishEventMediatorFactory.getTagName() + " mediator");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + messageContext.getEnvelope());
}
}
ActivityIDSetter.setActivityIdInTransportHeader(messageContext);
try {
Object[] metaData = new Object[metaProperties.size()];
for (int i = 0; i < metaProperties.size(); ++i) {
metaData[i] = metaProperties.get(i).extractPropertyValue(messageContext);
}
Object[] correlationData = new Object[correlationProperties.size()];
for (int i = 0; i < correlationProperties.size(); ++i) {
correlationData[i] = correlationProperties.get(i).extractPropertyValue(messageContext);
}
Object[] payloadData = new Object[payloadProperties.size()];
for (int i = 0; i < payloadProperties.size(); ++i) {
payloadData[i] = payloadProperties.get(i).extractPropertyValue(messageContext);
}
Map<String, String> arbitraryData = new HashMap<String, String>();
for (int i = 0; i < arbitraryProperties.size(); ++i) {
Property arbitraryProperty = arbitraryProperties.get(i);
arbitraryData.put(arbitraryProperty.getKey(),
arbitraryProperty.extractPropertyValue(messageContext).toString());
}
eventSink.getDataPublisher()
.publish(DataBridgeCommonsUtils.generateStreamId(getStreamName(), getStreamVersion()), metaData,
correlationData, payloadData, arbitraryData);
} catch (SynapseException e) {
String errorMsg = "Error occurred while constructing the event: " + e.getLocalizedMessage();
handleException(errorMsg, e, messageContext);
} catch (Exception e) {
String errorMsg = "Error occurred while sending the event: " + e.getLocalizedMessage();
handleException(errorMsg, e, messageContext);
}
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("End : " + PublishEventMediatorFactory.getTagName() + " mediator");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + messageContext.getEnvelope());
}
}
return true;
}
/**
* Finds the event sink by eventSinkName and sets the stream definition to the data publisher of event sink
*
* @return Found EventSink
*/
private EventSink loadEventSink() throws SynapseException {
if (eventSinkService == null) {
Object serviceObject = PrivilegedCarbonContext
.getThreadLocalCarbonContext().getOSGiService(EventSinkService.class);
if (serviceObject instanceof EventSinkService) {
eventSinkService = (EventSinkService) serviceObject;
} else {
throw new SynapseException("Internal error occurred. Failed to obtain EventSinkService");
}
}
EventSink eventSink = eventSinkService.getEventSink(getEventSinkName());
if (eventSink == null) {
throw new SynapseException("Event sink \"" + getEventSinkName() + "\" not found");
}
try {
StreamDefinition streamDef = new StreamDefinition(getStreamName(), getStreamVersion());
streamDef.setCorrelationData(generateAttributeList(getCorrelationProperties()));
streamDef.setMetaData(generateAttributeList(getMetaProperties()));
streamDef.setPayloadData(generateAttributeList(getPayloadProperties()));
} catch (MalformedStreamDefinitionException e) {
String errorMsg = "Failed to set stream definition. Malformed Stream Definition: " + e.getMessage();
throw new SynapseException(errorMsg, e);
} catch (Exception e) {
String errorMsg = "Error occurred while creating the Stream Definition: " + e.getMessage();
throw new SynapseException(errorMsg, e);
}
return eventSink;
}
/**
* Creates a list of data-bridge attributes for the given property list.
*
* @param propertyList List of properties for which attribute list should be created.
* @return Created data-bridge attribute list.
*/
private List<Attribute> generateAttributeList(List<Property> propertyList) throws SynapseException {
List<Attribute> attributeList = new ArrayList<Attribute>();
for (Property property : propertyList) {
attributeList.add(new Attribute(property.getKey(), property.getDatabridgeAttributeType()));
}
return attributeList;
}
public EventSink getEventSink() {
return eventSink;
}
public String getEventSinkName() {
return eventSinkName;
}
public String getStreamName() {
return streamName;
}
public String getStreamVersion() {
return streamVersion;
}
public List<Property> getMetaProperties() {
return metaProperties;
}
public List<Property> getCorrelationProperties() {
return correlationProperties;
}
public List<Property> getPayloadProperties() {
return payloadProperties;
}
public List<Property> getArbitraryProperties() {
return arbitraryProperties;
}
public void setEventSink(EventSink eventSink) {
this.eventSink = eventSink;
}
public void setEventSinkName(String eventSinkName) {
this.eventSinkName = eventSinkName;
}
public void setStreamName(String streamName) {
this.streamName = streamName;
}
public void setStreamVersion(String streamVersion) {
this.streamVersion = streamVersion;
}
public void setMetaProperties(List<Property> metaProperties) {
this.metaProperties = metaProperties;
}
public void setCorrelationProperties(List<Property> correlationProperties) {
this.correlationProperties = correlationProperties;
}
public void setPayloadProperties(List<Property> payloadProperties) {
this.payloadProperties = payloadProperties;
}
public void setArbitraryProperties(List<Property> arbitraryProperties) {
this.arbitraryProperties = arbitraryProperties;
}
}