com.prowidesoftware.swift.model.mx.AbstractMX.java Source code

Java tutorial

Introduction

Here is the source code for com.prowidesoftware.swift.model.mx.AbstractMX.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Prowide Inc.
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as 
 *     published by the Free Software Foundation, either version 3 of the 
 *     License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 *     
 *     Check the LGPL at <http://www.gnu.org/licenses/> for more details.
 *******************************************************************************/
package com.prowidesoftware.swift.model.mx;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.prowidesoftware.swift.Resolver;
import com.prowidesoftware.swift.io.parser.MxParser;
import com.prowidesoftware.swift.model.AbstractMessage;
import com.prowidesoftware.swift.model.MessageStandardType;
import com.prowidesoftware.swift.model.MxId;
import com.prowidesoftware.swift.model.mt.AbstractMT;
import com.prowidesoftware.swift.utils.Lib;

/**
 * Base class for specific MX messages.<br />
 *
 * IMPORTANT: An MX message is conformed by a set of optional headers 
 * and a message payload or document with the actual specific MX message. 
 * The name of the envelope element that binds a Header to the message 
 * to which it applies is <b>implementation/network specific</b> and not
 * part of the scope of this model. 
 * <br/>
 * This class provides the base container model for MX messages including
 * an attribute for the header. Further it supports both versions for the
 * header; the SWIFT Application Header (legacy) and the ISO Business
 * Application Header.
 * <br />
 * Subclasses in Prowide Integrator SDK implement the Document portion 
 * of each specific MX message type.
 * <br />
 * Serialization of this model into XML text can be done for the with or without
 * the header portion. When the header is set and included into the serialization, 
 * the container root element must be provided.
 *
 * @author www.prowidesoftware.com
 * @since 7.6
 * @see AbstractMT
 */
public abstract class AbstractMX extends AbstractMessage implements IDocument {
    private static final transient Logger log = Logger.getLogger(AbstractMX.class.getName());

    protected AbstractMX() {
        super(MessageStandardType.MX);
        // prevent construction
    }

    protected AbstractMX(final BusinessHeader businessHeader) {
        super(MessageStandardType.MX);
        this.businessHeader = businessHeader;
    }

    /**
     * Header portion of the message payload, common to all specific MX subclasses.
     * This information is required before opening the actual message to process the content properly.
     * @since 7.7
     */
    private BusinessHeader businessHeader;

    // TODO message is MT parse
    protected static String message(final String namespace, final AbstractMX obj,
            @SuppressWarnings("rawtypes") final Class[] classes, final String prefix,
            boolean includeXMLDeclaration) {
        return Resolver.mxWrite().message(namespace, obj, classes, prefix, includeXMLDeclaration);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected static AbstractMX read(final Class<? extends AbstractMX> targetClass, final String xml,
            final Class[] classes) {
        return Resolver.mxRead().read(targetClass, xml, classes);
    }

    /**
     * Get the classes associated with this message
     * @since 7.7
     */
    @SuppressWarnings("rawtypes")
    public abstract Class[] getClasses();

    /**
     * Get the XML namespace of the message
     * @since 7.7
     */
    public abstract String getNamespace();

    /**
     * get the Alphabetic code in four positions (fixed length) identifying the Business Process
     * @return the business process of the implementing class
     * @since 7.7
     */
    public abstract String getBusinessProcess();

    /**
     * Get the code identifying the Message Functionality
     * @return the set functionality or null if not set
     * @since 7.7
     */
    public abstract int getFunctionality();

    /**
     * Get the Message variant
     * @return the set variant or null if not set
     * @since 7.7
     */
    public abstract int getVariant();

    /**
     * Get the message version
     * @return the set vesion or null if not set
     * @since 7.7
     */
    public abstract int getVersion();

    /**
     * Get this message document as an XML string (headers not included).
     * The XML will include the XML declaration, the corresponding
     * namespace and a "Doc" prefix for the namespace.
     * 
     * @see #message(String, boolean)
     * @since 7.7
     */
    public String message() {
        return message(getNamespace(), this, getClasses(), "Doc", true);
    }

    /**
     * Get this message as an XML string.
     * <br> 
     * If the business header is set, the created XML will include
     * both the header and the document elements, under a default 
     * root element.
     * <br>
     * If the header is not present, the created XMl will only include
     * the document.
     * <br>
     * Both header and document are generated with namespace declaration
     * and default prefixes.
     * <br>
     * IMPORTANT: The name of the envelope element that binds a Header to 
     * the message to which it applies is implementation/network specific. 
     * The header root element AppHdr and the ISO 20022 MessageDefinition 
     * root element Document must always be sibling elements in any XML 
     * document, with the AppHdr element preceding the Document element.
     * <br>
     * 
     * @param rootElement optional specification of the root element
     * @return created XML
     * @since 7.8
     */
    public String message(final String rootElement, boolean includeXMLDeclaration) {
        StringBuilder xml = new StringBuilder();
        if (includeXMLDeclaration) {
            xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        }
        final String header = header("h", false);
        if (header != null) {
            xml.append("<" + rootElement + ">\n");
            xml.append(header + "\n");
        }
        xml.append(document("Doc", false) + "\n");
        if (header != null) {
            xml.append("</" + rootElement + ">");
        }
        return xml.toString();
    }

    /**
     * Same as {@linkplain #message(String)} with includeXMLDeclaration set to true
     * @since 7.8
     */
    public String message(final String rootElement) {
        return message(rootElement, true);
    }

    /**
     * Get this message business header as an XML string.
     * <br>
     * The XML will not include the XML declaration, and will
     * include de namespace as default (without prefix).
     * 
     * @return the serialized header or null if header is not set
     * @since 7.8
     * @see #header(String, boolean)
     */
    public String header() {
        return header(null, false);
    }

    /**
     * Get this message business header as an XML string.
     * <br>
     * @param prefix optional prefix for namespace (empty by default)
     * @param includeXMLDeclaration true to include the XML declaration (false by default)
     * @return header serialized into XML string or null if neither header version is present
     * @since 7.8
     * @see BusinessHeader#xml(String, boolean)
     */
    public String header(final String prefix, boolean includeXMLDeclaration) {
        if (this.businessHeader != null) {
            return this.businessHeader.xml(prefix, includeXMLDeclaration);
        } else {
            return null;
        }
    }

    /**
     * Get this message document as an XML string.
     * Same as {@link #message()}
     * @since 7.8
     */
    public String document() {
        return message();
    };

    /**
     * Get this message document as an XML string.
     * <br>
     * @param prefix optional prefix for namespace (empty by default)
     * @param includeXMLDeclaration true to include the XML declaration (false by default)
     * @return document serialized into XML string or null if errors occur during serialization
     * @since 7.8
     */
    public String document(final String prefix, boolean includeXMLDeclaration) {
        return message(getNamespace(), this, getClasses(), prefix, includeXMLDeclaration);
    }

    /**
     * Convenience method to get this message XML as javax.xml.transform.Source.
     * Notice this method will return only the document element of the message (headers not included).
     * 
     * @return <code>null</code> if message() returns <code>null</code> or StreamSource in other case
     * @since 7.7
     * @see #message()
     */
    public Source xmlSource() {
        final String xml = message();
        log.fine("XML: " + xml);
        if (xml != null) {
            return new StreamSource(new StringReader(xml));
        }
        return null;
    }

    /**
     * Writes the message document content into a file in XML format (headers not included).
     *
     * @param file a non null file to write, if it does not exists, it will be created
     * @since 7.7
     */
    public void write(final File file) throws IOException {
        Validate.notNull(file, "the file to write cannot be null");
        if (!file.exists()) {
            file.createNewFile();
        }
        final FileOutputStream stream = new FileOutputStream(file.getAbsoluteFile());
        write(stream);
    };

    /**
      * Writes the message document content into a file in XML format, encoding content in UTF-8 (headers not included).
     *
     * @param stream a non null stream to write
     * @since 7.7
     */
    public void write(final OutputStream stream) throws IOException {
        Validate.notNull(stream, "the stream to write cannot be null");
        stream.write(message().getBytes("UTF-8"));
    }

    /**
     * @since 7.7
     * @return the business header or null if not set
     */
    @XmlTransient
    public BusinessHeader getBusinessHeader() {
        return businessHeader;
    }

    /**
     * @since 7.8
     * @param businessHeader the header to set
     */
    public void setBusinessHeader(final BusinessHeader businessHeader) {
        this.businessHeader = businessHeader;
    };

    /**
     * Returns the MX message identification.<br>
     * Composed by the business process, functionality, variant and version.
     *
     * @return the constructed message id
     * @since 7.7
     */
    public MxId getMxId() {
        return new MxId(getBusinessProcess(), StringUtils.leftPad("" + getFunctionality(), 3, "0"),
                StringUtils.leftPad("" + getVariant(), 3, "0"), StringUtils.leftPad("" + getVersion(), 2, "0"));
    }

    public Element element() {
        final HashMap<String, String> properties = new HashMap<String, String>();
        // it didn't work as expected
        // properties.put(JAXBRIContext.DEFAULT_NAMESPACE_REMAP, namespace);
        try {
            JAXBContext context = JAXBContext.newInstance(getClasses(), properties);

            DOMResult res = new DOMResult();
            context.createMarshaller().marshal(this, res);
            Document doc = (Document) res.getNode();

            return (Element) doc.getFirstChild();
        } catch (Exception e) {
            log.log(Level.WARNING, "Error creating XML Document for MX", e);
            return null;
        }
    }

    /**
     * Parses the XML string containing the Document element of an MX message into a specific instance of MX message object.
     * <br />
     * If the string is empty, does not contain an MX document, the message type cannot be 
     * detected or an error occur reading and parsing the message content; this method returns null.
     * <br />
     * The implementation detects the message type and uses reflection to call the
     * parser in the specific Mx subclass.
     * <br />
     * IMPORTANT: For the moment this is supported only in Prowide Integrator.
     * To parse XML into the generic MxNode structure, or to parse business headers check {@linkplain MxParser}
     * 
     * @param xml string a string containing the Document of an MX message in XML format
     * @param id optional parameter to indicate the specific MX type to create; autodetected from namespace if null.
     * @return parser message or null if string content could not be parsed into an Mx
     * 
     * @since 7.8.4
     */
    public static AbstractMX parse(final String xml, MxId id) {
        return Resolver.mxRead().read(xml, id);
    }

    /**
     * Parses a file content into a specific instance of Mx. 
     * <br />
     * IMPORTANT: For the moment this is supported only in Prowide Integrator.
     * To parse XML into the generic MxNode structure, or to parse business headers check {@linkplain MxParser}
     * 
     * @param file a file containing a swift MX message
      * @param id optional parameter to indicate the specific MX type to create; autodetected from namespace if null.
     * @return parser message or null if file content could not be parsed
     * @see #parse(String, MxId)
     * 
     * @since 7.8.4
     */
    public static AbstractMX parse(final File file, MxId id) throws IOException {
        return parse(Lib.readFile(file), id);
    }

}