/* * Copyright (c) 2015, 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.event.publisher.core.internal.type.xml; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.util.AXIOMUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.databridge.commons.Attribute; import org.wso2.carbon.databridge.commons.StreamDefinition; import org.wso2.carbon.event.publisher.core.config.EventPublisherConfiguration; import org.wso2.carbon.event.publisher.core.config.EventPublisherConstants; import org.wso2.carbon.event.publisher.core.config.mapping.XMLOutputMapping; import org.wso2.carbon.event.publisher.core.exception.EventPublisherConfigurationException; import org.wso2.carbon.event.publisher.core.internal.OutputMapper; import org.wso2.carbon.event.publisher.core.internal.util.EventPublisherUtil; import org.wso2.carbon.event.publisher.core.internal.util.RuntimeResourceLoader; import org.wso2.siddhi.core.event.Event; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import java.util.ArrayList; import java.util.List; import java.util.Map; public class XMLOutputMapper implements OutputMapper { private static final Log log = LogFactory.getLog(XMLOutputMapper.class); private EventPublisherConfiguration eventPublisherConfiguration = null; private Map<String, Integer> propertyPositionMap = null; private List<String> mappingTextList; private boolean isCustomRegistryPath; private final RuntimeResourceLoader runtimeResourceLoader; private final boolean isCustomMappingEnabled; private String mappingText; public XMLOutputMapper(EventPublisherConfiguration eventPublisherConfiguration, Map<String, Integer> propertyPositionMap, int tenantId, StreamDefinition streamDefinition) throws EventPublisherConfigurationException { this.eventPublisherConfiguration = eventPublisherConfiguration; this.propertyPositionMap = propertyPositionMap; XMLOutputMapping outputMapping = (XMLOutputMapping) eventPublisherConfiguration.getOutputMapping(); this.runtimeResourceLoader = new RuntimeResourceLoader(outputMapping.getCacheTimeoutDuration(), propertyPositionMap); this.isCustomMappingEnabled = outputMapping.isCustomMappingEnabled(); if (this.isCustomMappingEnabled) { this.mappingText = getCustomMappingText(); } else { this.mappingText = generateTemplateXMLEvent(streamDefinition); } if (!outputMapping.isRegistryResource()) { // Store only if it is not from registry this.mappingText = validateXML(mappingText); this.mappingTextList = generateMappingTextList(mappingText); } } private String validateXML(String text) throws EventPublisherConfigurationException { try { //Parsing and converting back to string to discover parse exceptions early. OMElement mappingOMElement = AXIOMUtil.stringToOM(text); return mappingOMElement.toString(); } catch (XMLStreamException e) { throw new EventPublisherConfigurationException("Could not parse the mapping text:" + e.getMessage(), e); } } private List<String> getOutputMappingPropertyList(String mappingText) throws EventPublisherConfigurationException { List<String> mappingTextList = new ArrayList<String>(); String text = mappingText; int prefixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX); int postfixIndex; while (prefixIndex > 0) { postfixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX); if (postfixIndex > prefixIndex) { mappingTextList.add(text.substring(prefixIndex + 2, postfixIndex)); text = text.substring(postfixIndex + 2); } else { throw new EventPublisherConfigurationException("Found template attribute prefix " + EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX + " without corresponding postfix " + EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX + ". Please verify your XML template."); } prefixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX); } return mappingTextList; } private List<String> generateMappingTextList(String mappingText) throws EventPublisherConfigurationException { List<String> mappingTextList = new ArrayList<String>(); String text = mappingText; int prefixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX); int postfixIndex; while (prefixIndex > 0) { postfixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX); if (postfixIndex > prefixIndex) { mappingTextList.add(text.substring(0, prefixIndex)); mappingTextList.add(text.substring(prefixIndex + 2, postfixIndex)); text = text.substring(postfixIndex + 2); } else { throw new EventPublisherConfigurationException("Found template attribute prefix " + EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX + " without corresponding postfix " + EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX + ". Please verify your XML template."); } prefixIndex = text.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX); } mappingTextList.add(text); return mappingTextList; } private String getCustomMappingText() throws EventPublisherConfigurationException { XMLOutputMapping textOutputMapping = ((XMLOutputMapping) eventPublisherConfiguration.getOutputMapping()); String actualMappingText = textOutputMapping.getMappingXMLText(); if (textOutputMapping.isRegistryResource()) { this.isCustomRegistryPath = actualMappingText.contains(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX) && actualMappingText.indexOf(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX) > 0; if (!this.isCustomRegistryPath) { actualMappingText = this.runtimeResourceLoader.getResourceContent(textOutputMapping.getMappingXMLText()); } } return actualMappingText; } private String getPropertyValue(Event event, String mappingProperty) { Object[] eventData = event.getData(); Map<String, Object> arbitraryMap = event.getArbitraryDataMap(); Integer position = propertyPositionMap.get(mappingProperty); Object data = null; if (position != null && eventData.length != 0) { data = eventData[position]; } else if (mappingProperty != null && arbitraryMap != null && arbitraryMap.containsKey(mappingProperty)) { data = arbitraryMap.get(mappingProperty); } if (data != null) { return data.toString(); } return ""; } @Override public Object convertToMappedInputEvent(Event event) throws EventPublisherConfigurationException { if (this.isCustomMappingEnabled) { EventPublisherUtil.validateStreamDefinitionWithOutputProperties(mappingText, propertyPositionMap, event.getArbitraryDataMap()); } // Retrieve resource at runtime if it is from registry XMLOutputMapping outputMapping = (XMLOutputMapping) eventPublisherConfiguration.getOutputMapping(); if (outputMapping.isRegistryResource()) { String path = outputMapping.getMappingXMLText(); if (isCustomRegistryPath) { // Retrieve the actual path List<String> pathMappingTextList = generateMappingTextList(path); StringBuilder pathBuilder = new StringBuilder(pathMappingTextList.get(0)); for (int i = 1; i < pathMappingTextList.size(); i++) { if (i % 2 == 0) { pathBuilder.append(pathMappingTextList.get(i)); } else { pathBuilder.append(getPropertyValue(event, pathMappingTextList.get(i))); } } path = pathBuilder.toString(); } // Retrieve actual content String actualMappingText = this.runtimeResourceLoader.getResourceContent(path); // Validate XML actualMappingText = validateXML(actualMappingText); this.mappingTextList = generateMappingTextList(actualMappingText); } StringBuilder eventText = new StringBuilder(mappingTextList.get(0)); for (int i = 1, size = mappingTextList.size(); i < size; i++) { if (i % 2 == 0) { eventText.append(mappingTextList.get(i)); } else { eventText.append(getPropertyValue(event, mappingTextList.get(i))); } } String text = eventText.toString(); if (!this.isCustomMappingEnabled) { Map<String, Object> arbitraryDataMap = event.getArbitraryDataMap(); if (arbitraryDataMap != null && !arbitraryDataMap.isEmpty()) { // Add arbitrary data map to the default template try { OMFactory factory = OMAbstractFactory.getOMFactory(); OMElement compositeEventElement = AXIOMUtil.stringToOM(text); OMElement parentPropertyElement = factory.createOMElement(new QName(EventPublisherConstants.EVENT_ARBITRARY_DATA_MAP_TAG)); for (Map.Entry<String, Object> entry : arbitraryDataMap.entrySet()) { OMElement propertyElement = factory.createOMElement(new QName(entry.getKey())); propertyElement.setText(entry.getValue().toString()); parentPropertyElement.addChild(propertyElement); } compositeEventElement.getFirstElement().addChild(parentPropertyElement); text = compositeEventElement.toString(); } catch (XMLStreamException e) { log.warn("Error in parsing event XML text", e); } } } return text; } @Override public Object convertToTypedInputEvent(Event event) throws EventPublisherConfigurationException { return convertToMappedInputEvent(event); } private String generateTemplateXMLEvent(StreamDefinition streamDefinition) { OMFactory factory = OMAbstractFactory.getOMFactory(); OMElement compositeEventElement = factory.createOMElement(new QName( EventPublisherConstants.MULTIPLE_EVENTS_PARENT_TAG)); OMElement templateEventElement = factory.createOMElement(new QName(EventPublisherConstants.EVENT_PARENT_TAG)); compositeEventElement.addChild(templateEventElement); List<Attribute> metaDatAttributes = streamDefinition.getMetaData(); if (metaDatAttributes != null && metaDatAttributes.size() > 0) { templateEventElement.addChild(createPropertyElement(factory, EventPublisherConstants.PROPERTY_META_PREFIX, metaDatAttributes, EventPublisherConstants.EVENT_META_TAG)); } List<Attribute> correlationAttributes = streamDefinition.getCorrelationData(); if (correlationAttributes != null && correlationAttributes.size() > 0) { templateEventElement.addChild(createPropertyElement(factory, EventPublisherConstants.PROPERTY_CORRELATION_PREFIX, correlationAttributes, EventPublisherConstants.EVENT_CORRELATION_TAG)); } List<Attribute> payloadAttributes = streamDefinition.getPayloadData(); if (payloadAttributes != null && payloadAttributes.size() > 0) { templateEventElement.addChild(createPropertyElement(factory, "", payloadAttributes, EventPublisherConstants.EVENT_PAYLOAD_TAG)); } return compositeEventElement.toString(); } private static OMElement createPropertyElement(OMFactory factory, String dataPrefix, List<Attribute> attributeList, String propertyTag) { OMElement parentPropertyElement = factory.createOMElement(new QName( propertyTag)); for (Attribute attribute : attributeList) { OMElement propertyElement = factory.createOMElement(new QName( attribute.getName())); propertyElement.setText(EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_PREFIX + dataPrefix + attribute.getName() + EventPublisherConstants.TEMPLATE_EVENT_ATTRIBUTE_POSTFIX); parentPropertyElement.addChild(propertyElement); } return parentPropertyElement; } }