All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlasmap.xml.module.XmlModule Maven / Gradle / Ivy

/**
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * Licensed 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 io.atlasmap.xml.module;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import io.atlasmap.api.AtlasConversionException;
import io.atlasmap.api.AtlasException;
import io.atlasmap.api.AtlasSession;
import io.atlasmap.api.AtlasValidationException;
import io.atlasmap.core.AtlasUtil;
import io.atlasmap.core.BaseAtlasModule;
import io.atlasmap.core.PathUtil;
import io.atlasmap.core.PathUtil.SegmentContext;
import io.atlasmap.spi.AtlasModuleDetail;
import io.atlasmap.v2.Audit;
import io.atlasmap.v2.AuditStatus;
import io.atlasmap.v2.BaseMapping;
import io.atlasmap.v2.ConstantField;
import io.atlasmap.v2.DataSource;
import io.atlasmap.v2.DataSourceType;
import io.atlasmap.v2.Field;
import io.atlasmap.v2.FieldType;
import io.atlasmap.v2.Mapping;
import io.atlasmap.v2.PropertyField;
import io.atlasmap.v2.SimpleField;
import io.atlasmap.v2.Validation;
import io.atlasmap.xml.v2.AtlasXmlModelFactory;
import io.atlasmap.xml.core.XmlFieldReader;
import io.atlasmap.xml.core.XmlFieldWriter;
import io.atlasmap.xml.v2.XmlDataSource;
import io.atlasmap.xml.v2.XmlField;
import io.atlasmap.xml.v2.XmlNamespace;
import io.atlasmap.xml.v2.XmlNamespaces;

@AtlasModuleDetail(name = "XmlModule", uri = "atlas:xml", modes = { "SOURCE", "TARGET" }, dataFormats = {
        "xml" }, configPackages = { "io.atlasmap.xml.v2" })
public class XmlModule extends BaseAtlasModule {
    private static final Logger logger = LoggerFactory.getLogger(XmlModule.class);

    @Override
    public void processPreOutputExecution(AtlasSession session) throws AtlasException {
        XmlNamespaces xmlNs = null;
        String template = null;
        for (DataSource ds : session.getMapping().getDataSource()) {
            if (DataSourceType.TARGET.equals(ds.getDataSourceType()) && ds instanceof XmlDataSource) {
                xmlNs = ((XmlDataSource) ds).getXmlNamespaces();
                template = ((XmlDataSource) ds).getTemplate();
            }
        }

        Map nsMap = new HashMap();
        if (xmlNs != null && xmlNs.getXmlNamespace() != null && !xmlNs.getXmlNamespace().isEmpty()) {
            for (XmlNamespace ns : xmlNs.getXmlNamespace()) {
                nsMap.put(ns.getAlias(), ns.getUri());
            }
        }

        XmlFieldWriter writer = new XmlFieldWriter(nsMap, template);
        session.setOutput(writer);

        if (logger.isDebugEnabled()) {
            logger.debug("processPreOutputExcution completed");
        }
    }

    @Override
    public void processPreValidation(AtlasSession atlasSession) throws AtlasException {
        if (atlasSession == null || atlasSession.getMapping() == null) {
            logger.error("Invalid session: Session and AtlasMapping must be specified");
            throw new AtlasValidationException("Invalid session");
        }

        XmlValidationService xmlValidationService = new XmlValidationService(getConversionService());
        List xmlValidations = xmlValidationService.validateMapping(atlasSession.getMapping());
        atlasSession.getValidations().getValidation().addAll(xmlValidations);

        if (logger.isDebugEnabled()) {
            logger.debug("Detected " + xmlValidations.size() + " xml validation notices");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("processPreValidation completed");
        }
    }

    @Override
    public void processInputMapping(AtlasSession session, BaseMapping baseMapping) throws AtlasException {
        for (Mapping mapping : this.generateInputMappings(session, baseMapping)) {
            if (mapping.getInputField() == null || mapping.getInputField().isEmpty()) {
                addAudit(session, null,
                        String.format("Mapping does not contain at least one input field alias=%s desc=%s",
                                mapping.getAlias(), mapping.getDescription()),
                        null, AuditStatus.WARN, null);
                return;
            }

            for (Field field : mapping.getInputField()) {

                if (!isSupportedField(field)) {
                    addAudit(session, field.getDocId(),
                            String.format("Unsupported input field type=%s", field.getClass().getName()), field.getPath(),
                            AuditStatus.ERROR, null);
                    return;
                }

                if (field instanceof ConstantField) {
                    processConstantField(session, mapping);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Processed input constantField sPath=" + field.getPath() + " sV=" + field.getValue()
                        + " sT=" + field.getFieldType() + " docId: " + field.getDocId());
                    }
                    continue;
                }

                if (field instanceof PropertyField) {
                    processPropertyField(session, mapping,
                            session.getAtlasContext().getContextFactory().getPropertyStrategy());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Processed input propertyField sPath=" + field.getPath() + " sV=" + field.getValue()
                        + " sT=" + field.getFieldType() + " docId: " + field.getDocId());
                    }
                    continue;
                }

                XmlField inputField = (XmlField) field;

                Object sourceObject = null;
                if (field.getDocId() != null && session.hasInput(field.getDocId())) {
                    // Use docId only when it exists, otherwise use default input
                    sourceObject = session.getInput(field.getDocId());
                } else {
                    sourceObject = session.getInput();
                }

                if (sourceObject == null || !(sourceObject instanceof String)) {
                    addAudit(session, field.getDocId(),
                            String.format("Unsupported input object type=%s", field.getClass().getName()), field.getPath(),
                            AuditStatus.ERROR, null);
                    return;
                }

                Document document = null;

                Map sourceUriParams = AtlasUtil
                        .getUriParameters(session.getMapping().getDataSource().get(0).getUri());

                boolean enableNamespaces = true;
                for (String key : sourceUriParams.keySet()) {
                    if ("disableNamespaces".equals(key)) {
                        if ("true".equals(sourceUriParams.get("disableNamespaces"))) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Disabling namespace support");
                            }
                            enableNamespaces = false;
                        }
                    }
                }

                try {
                    document = getDocument((String) sourceObject, enableNamespaces);
                } catch (IOException | ParserConfigurationException | SAXException e) {
                    logger.error(String.format("Error parsing xml input object msg=%s", e.getMessage()), e);
                    Audit audit = new Audit();
                    audit.setDocId(field.getDocId());
                    audit.setPath(field.getPath());
                    audit.setStatus(AuditStatus.ERROR);
                    audit.setMessage(String.format("Error parsing xml input object msg=%s", field.getClass().getName()));
                    session.getAudits().getAudit().add(audit);
                    return;
                }

                XmlFieldReader dxfr = new XmlFieldReader();
                dxfr.readNew(document, inputField);

                if (inputField.getFieldType() == null) {
                    inputField.setFieldType(FieldType.STRING);
                }

                if (logger.isDebugEnabled()) {
                    logger.debug("Processed input field sPath=" + field.getPath() + " sV=" + field.getValue() + " sT="
                            + field.getFieldType() + " docId: " + field.getDocId());
                }
            }
        }
    }

    @Override
    public void processOutputMapping(AtlasSession session, BaseMapping baseMapping) throws AtlasException {

        XmlFieldWriter writer = null;
        if (session.getOutput() == null) {
            writer = new XmlFieldWriter();
            session.setOutput(writer);
        } else if (session.getOutput() != null && session.getOutput() instanceof XmlFieldWriter) {
            writer = (XmlFieldWriter) session.getOutput();
        } else {
            addAudit(session, null,
                    String.format("Unsupported output object type=%s", session.getOutput().getClass().getName()), null,
                    AuditStatus.ERROR, null);
            return;
        }

        for (Mapping mapping : this.getOutputMappings(session, baseMapping)) {
            if (mapping.getOutputField() == null || mapping.getOutputField().isEmpty()) {
                addAudit(session, null,
                        String.format("Mapping does not contain at least one output field alias=%s desc=%s",
                                mapping.getAlias(), mapping.getDescription()),
                        null, AuditStatus.ERROR, null);
                return;
            }

            Field outputField = mapping.getOutputField().get(0);
            if (!(outputField instanceof XmlField)) {
                addAudit(session, outputField.getDocId(),
                        String.format("Unsupported output field type=%s", outputField.getClass().getName()),
                        outputField.getPath(), AuditStatus.ERROR, null);
                return;
            }

            switch (mapping.getMappingType()) {
            case MAP:
                Field inField = mapping.getInputField().get(0);
                if (inField.getValue() == null) {
                    continue;
                }

                // Attempt to Auto-detect field type based on input value
                if (outputField.getFieldType() == null && inField.getValue() != null) {
                    outputField.setFieldType(getConversionService().fieldTypeFromClass(inField.getValue().getClass()));
                }

                Object outputValue = null;

                // Do auto-conversion
                if (inField.getFieldType() != null && inField.getFieldType().equals(outputField.getFieldType())) {
                    outputValue = inField.getValue();
                } else {
                    try {
                        outputValue = getConversionService().convertType(inField.getValue(), inField.getFieldType(),
                                outputField.getFieldType());
                    } catch (AtlasConversionException e) {
                        logger.error(String.format("Unable to auto-convert for iT=%s oT=%s oF=%s msg=%s",
                                inField.getFieldType(), outputField.getFieldType(), outputField.getPath(),
                                e.getMessage()), e);
                        continue;
                    }
                }

                outputField.setValue(outputValue);

                if (outputField.getActions() != null && outputField.getActions().getActions() != null
                        && !outputField.getActions().getActions().isEmpty()) {
                    processFieldActions(session.getAtlasContext().getContextFactory().getFieldActionService(),
                            outputField);
                }

                writer.write((XmlField) outputField);
                break;
            case COMBINE:
                processCombineField(session, mapping, mapping.getInputField(), outputField);
                SimpleField combinedField = new SimpleField();
                combinedField.setFieldType(FieldType.STRING);
                combinedField.setPath(outputField.getPath());
                combinedField.setValue(outputField.getValue());

                if (combinedField.getActions() != null && combinedField.getActions().getActions() != null
                        && !combinedField.getActions().getActions().isEmpty()) {
                    processFieldActions(session.getAtlasContext().getContextFactory().getFieldActionService(),
                            combinedField);
                }

                writer.write(combinedField);
                break;
            case LOOKUP:
                Field inputFieldlkp = mapping.getInputField().get(0);
                if (inputFieldlkp.getValue() != null
                        && inputFieldlkp.getValue().getClass().isAssignableFrom(String.class)) {
                    processLookupField(session, mapping.getLookupTableName(), (String) inputFieldlkp.getValue(),
                            outputField);
                } else {
                    processLookupField(session, mapping.getLookupTableName(), (String) getConversionService()
                            .convertType(inputFieldlkp.getValue(), inputFieldlkp.getFieldType(), FieldType.STRING),
                            outputField);
                }

                if (outputField.getActions() != null && outputField.getActions().getActions() != null
                        && !outputField.getActions().getActions().isEmpty()) {
                    processFieldActions(session.getAtlasContext().getContextFactory().getFieldActionService(),
                            outputField);
                }

                writer.write(outputField);
                break;
            case SEPARATE:
                Field inputFieldsep = mapping.getInputField().get(0);
                for (Field outputFieldsep : mapping.getOutputField()) {
                    Field separateField = processSeparateField(session, mapping, inputFieldsep, outputFieldsep);
                    if (separateField == null) {
                        continue;
                    }

                    outputFieldsep.setValue(separateField.getValue());
                    if (outputFieldsep.getFieldType() == null) {
                        outputFieldsep.setFieldType(separateField.getFieldType());
                    }

                    if (outputFieldsep.getActions() != null && outputFieldsep.getActions().getActions() != null
                            && !outputFieldsep.getActions().getActions().isEmpty()) {
                        processFieldActions(session.getAtlasContext().getContextFactory().getFieldActionService(),
                                outputFieldsep);
                    }
                    writer.write(outputFieldsep);
                }
                break;
            default:
                logger.error("Unsupported mappingType=%s detected", mapping.getMappingType());
                return;
            }

            if (logger.isDebugEnabled()) {
                logger.debug(String.format("Processed output field oP=%s oV=%s oT=%s docId: %s", outputField.getPath(),
                        outputField.getValue(), outputField.getFieldType(), outputField.getDocId()));
            }
        }
    }

    @Override
    public void processPostOutputExecution(AtlasSession session) throws AtlasException {
        if (logger.isDebugEnabled()) {
            logger.debug("processPostOutputExecution completed");
        }

        Object output = session.getOutput();
        if (output != null) {
            if (output instanceof XmlFieldWriter) {
                session.setOutput(convertDocumentToString(((XmlFieldWriter) output).getDocument()));
            }
        }
    }

    @Override
    public Boolean isSupportedField(Field field) {
        if (field instanceof XmlField) {
            return true;
        } else if (field instanceof PropertyField) {
            return true;
        } else if (field instanceof ConstantField) {
            return true;
        } else if (field instanceof SimpleField) {
            return true;
        }
        return false;
    }

    private String convertDocumentToString(Document document) throws AtlasException {
        DocumentBuilderFactory domFact = DocumentBuilderFactory.newInstance();
        domFact.setNamespaceAware(true);

        StringWriter writer = null;
        try {
            DOMSource domSource = new DOMSource(document);
            writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.transform(domSource, result);
            return writer.toString();
        } catch (TransformerException e) {
            logger.error(String.format("Error converting Xml document to string msg=%s", e.getMessage()), e);
            throw new AtlasException(e.getMessage(), e);
        }
    }

    private Document getDocument(String data, boolean namespaced)
            throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(namespaced); // this must be done to use namespaces
        DocumentBuilder b = dbf.newDocumentBuilder();
        return b.parse(new ByteArrayInputStream(data.getBytes("UTF-8")));
    }

    @Override
    public int getCollectionSize(AtlasSession session, Field field) throws AtlasException {
        try {
            Object sourceObject = null;
            if (field.getDocId() != null && session.hasInput(field.getDocId())) {
                // Use docId only when it exists, otherwise use default input
                sourceObject = session.getInput(field.getDocId());
            } else {
                sourceObject = session.getInput();
            }
            Document document = getDocument((String) sourceObject, false);
            Element parentNode = document.getDocumentElement();
            for (SegmentContext sc : new PathUtil(field.getPath()).getSegmentContexts(false)) {
                if (sc.getPrev() == null) {
                    // processing root node part of path such as the "XOA" part of
                    // "/XOA/contact<>/firstName", skip.
                    continue;
                }
                String childrenElementName = PathUtil.cleanPathSegment(sc.getSegment());
                String namespaceAlias = PathUtil.getNamespace(sc.getSegment());
                if (namespaceAlias != null && !"".equals(namespaceAlias)) {
                    childrenElementName = namespaceAlias + ":" + childrenElementName;
                }
                List children = XmlFieldWriter.getChildrenWithName(childrenElementName, parentNode);
                if (children == null || children.isEmpty()) {
                    return 0;
                }
                if (PathUtil.isCollectionSegment(sc.getSegment())) {
                    return children.size();
                }
                parentNode = children.get(0);
            }
            return 0;
        } catch (Exception e) {
            throw new AtlasException(e);
        }
    }

    @Override
    public Field cloneField(Field field) throws AtlasException {
        return AtlasXmlModelFactory.cloneField(field);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy