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

Java tutorial

Introduction

Here is the source code for com.prowidesoftware.swift.model.mx.BusinessHeader.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.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.transform.dom.DOMResult;

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

import com.prowidesoftware.swift.io.parser.MxParser;
import com.prowidesoftware.swift.model.MxId;
import com.prowidesoftware.swift.model.mx.dic.ApplicationHeader;
import com.prowidesoftware.swift.model.mx.dic.BranchAndFinancialInstitutionIdentification5;
import com.prowidesoftware.swift.model.mx.dic.BusinessApplicationHeaderV01;
import com.prowidesoftware.swift.model.mx.dic.FinancialInstitutionIdentification8;
import com.prowidesoftware.swift.model.mx.dic.Party9Choice;

/**
 * General information applicable to any MX.
 *
 * The business header is an optional part of the payload of a message,
 * and contains information that is relevant to the business applications
 * that process the message.
 *
 * There are two different business standards, but a message can contain only one.
 * <ul>
 * <li>The ISO 20022 business application header: {@link BusinessApplicationHeaderV01}</li>
 * <li>The application header originally defined by swift: {@link ApplicationHeader}</li>
 * </ul>
 *
 * @author sebastian@prowidesoftware.com
 * @since 7.7
 */
public class BusinessHeader {
    private static final transient Logger log = Logger.getLogger(BusinessHeader.class.getName());

    private ApplicationHeader applicationHeader;
    private BusinessApplicationHeaderV01 businessApplicationHeader;

    public static final transient String NAMESPACE_AH = "urn:swift:xsd:$ahV10";
    public static final transient String NAMESPACE_BAH = "urn:iso:std:iso:20022:tech:xsd:head.001.001.01";

    /**
     * Creates an empty header
     */
    public BusinessHeader() {
        super();
    }

    /**
     * Creates a business header from the SWIFT Application Header
     * @param applicationHeader
     * @since 7.8
     */
    public BusinessHeader(final ApplicationHeader applicationHeader) {
        this();
        this.applicationHeader = applicationHeader;
    }

    /**
     * Creates a business header from the ISO Business Application Header
     * @param businessApplicationHeader
     * @since 7.8
     */
    public BusinessHeader(final BusinessApplicationHeaderV01 businessApplicationHeader) {
        this();
        this.businessApplicationHeader = businessApplicationHeader;
    }

    public ApplicationHeader getApplicationHeader() {
        return applicationHeader;
    }

    public void setApplicationHeader(final ApplicationHeader applicationHeader) {
        if (this.businessApplicationHeader != null) {
            throw new IllegalStateException(
                    "can't set applicationHeader when businessApplicationHeader is not null, set it to null before. These attributes overlap each other");
        }
        this.applicationHeader = applicationHeader;
    }

    public BusinessApplicationHeaderV01 getBusinessApplicationHeader() {
        return businessApplicationHeader;
    }

    public void setBusinessApplicationHeader(final BusinessApplicationHeaderV01 businessApplicationHeader) {
        if (this.applicationHeader != null) {
            throw new IllegalStateException(
                    "can't set businessApplicationHeader when applicationHeader is not null, set it to null before. These attributes overlap each other");
        }
        this.businessApplicationHeader = businessApplicationHeader;
    }

    /*
     *  2015.08 miguel.
     *  ver MX Headers/stdsmx_usgi.pdf
     *  doc 3.2.3 Correspondence between the ISO Business Application Header and the Application Header
     *  de ahi sintentizar los atributos aca.
     *  FIXME replicar metodos unificados segun el mapeo de la seccion 3.2.3
     */

    /**
     * Gets the sender BIC code
     * 
     * If the header is a BAH, gets the BIC code from BusinessApplicationHeaderV01/Fr/FIId/FinInstnId/BICFI
     * <br />
     * If the header is an AH, gets the same from ApplicationHeader/From/Type+Id where if Type
     * is BIC the Id is returned as is, otherwise the domain name is parsed using {@link MxParser#getBICFromDN(String)}
     * 
     * @return found BIC or null if not present or cannot be parsed
     */
    public String from() {
        if (this.applicationHeader == null) {
            if (this.businessApplicationHeader == null) {
                return null;
            }
            /*
             * is BAH
             */
            try {
                return this.businessApplicationHeader.getFr().getFIId().getFinInstnId().getBICFI();
            } catch (NullPointerException e) {
                return null;
            }
        } else {
            /*
             * is AH
             */
            try {
                if (StringUtils.equals(this.applicationHeader.getFrom().getType(), "BIC")) {
                    return this.applicationHeader.getFrom().getId();
                } else {
                    return MxParser.getBICFromDN(this.applicationHeader.getFrom().getId());
                }
            } catch (NullPointerException e) {
                return null;
            }
        }
    }

    /**
     * Gets the receiver BIC code
     * 
     * If the header is a BAH, gets the BIC code from BusinessApplicationHeaderV01/To/FIId/FinInstnId/BICFI
     * <br />
     * If the header is an AH, gets the same from ApplicationHeader/To/Type+Id where if Type
     * is BIC the Id is returned as is, otherwise the domain name is parsed using {@link MxParser#getBICFromDN(String)}
     * 
     * @return found BIC or null if not present or cannot be parsed
     */
    public String to() {
        if (this.applicationHeader == null) {
            if (this.businessApplicationHeader == null) {
                return null;
            }
            /*
             * is BAH
             */
            try {
                return this.businessApplicationHeader.getTo().getFIId().getFinInstnId().getBICFI();
            } catch (NullPointerException e) {
                return null;
            }
        } else {
            /*
             * is AH
             */
            try {
                if (StringUtils.equals(this.applicationHeader.getTo().getType(), "BIC")) {
                    return this.applicationHeader.getTo().getId();
                } else {
                    return MxParser.getBICFromDN(this.applicationHeader.getTo().getId());
                }
            } catch (NullPointerException e) {
                return null;
            }
        }
    }

    /**
     * Get the message reference.
     * 
     * @see BusinessApplicationHeaderV01#getBizMsgIdr()
     * @see ApplicationHeader#getMsgRef()
     * 
     * @since 7.8
     */
    public String reference() {
        if (this.applicationHeader == null) {
            if (this.businessApplicationHeader == null) {
                return null;
            }
            return this.businessApplicationHeader.getBizMsgIdr();
        }
        return this.applicationHeader.getMsgRef();
    }

    /**
     * Get this header as an XML string.
     * 
     * @return header serialized into XML string or null if neither header version is present
     * @since 7.8
     * @see #xml(String, boolean)
     */
    public String xml() {
        return xml(null, false);
    }

    /**
     * Get this header as an XML string.
     * Since this class contains a dual model supporting two type of headers (swift and ISO), if both
     * headers are present in the object the BusinessApplicationHeaderV01 will be used.
     * 
     * @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
     */
    public String xml(final String prefix, boolean includeXMLDeclaration) {
        Object header = null;
        if (this.businessApplicationHeader != null) {
            header = this.businessApplicationHeader;
        } else if (this.applicationHeader != null) {
            header = this.applicationHeader;
        } else {
            return null;
        }
        try {
            JAXBContext context = JAXBContext.newInstance(header.getClass());
            final Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            final StringWriter sw = new StringWriter();
            marshaller.marshal(_element(header), new XmlEventWriter(sw, prefix, includeXMLDeclaration, "AppHdr"));
            return sw.getBuffer().toString();

        } catch (JAXBException e) {
            log.log(Level.SEVERE, "Error writing XML:" + e + "\n for header: " + header);
        }
        return null;
    }

    /**
     * Gets the header as an Element object.
     *  
     * @return Element this header parsed into Element or null if header is null
     * @since 7.8
     */
    public Element element() {
        Object header = null;
        if (this.businessApplicationHeader != null) {
            header = this.businessApplicationHeader;
        } else if (this.applicationHeader != null) {
            header = this.applicationHeader;
        } else {
            return null;
        }
        try {
            JAXBContext context = JAXBContext.newInstance(header.getClass());
            final Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            DOMResult res = new DOMResult();
            marshaller.marshal(_element(header), res);
            Document doc = (Document) res.getNode();
            return (Element) doc.getFirstChild();

        } catch (JAXBException e) {
            log.log(Level.SEVERE, "Error writing XML:" + e + "\n for header: " + header);
        }
        return null;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private JAXBElement _element(final Object header) {
        if (header instanceof BusinessApplicationHeaderV01) {
            return (JAXBElement<BusinessApplicationHeaderV01>) new JAXBElement(new QName(NAMESPACE_BAH, "AppHdr"),
                    header.getClass(), null, header);
        } else if (header instanceof ApplicationHeader) {
            return (JAXBElement<ApplicationHeader>) new JAXBElement(new QName(NAMESPACE_AH, "AppHdr"),
                    header.getClass(), null, header);
        } else {
            return null;
        }
    }

    /**
     * Convenient method to create a new header, initialized from simple parameters.
     * <br>
     * The created header will be of type {@link BusinessApplicationHeaderV01}.
     * Creation date will be set to current time.
     * <br />
     * All parameters are optional but in order for the header to be valid the
     * sender, receiver and reference must be set.
     * 
     * @param sender optional sender BIC for the Fr element or <code>null</code> to leave not set
     * @param receiver optional receiver BIC for the To element or <code>null</code> to leave not set
     * @param reference optional reference for the BizMsgIdr (business message identifier) or <code>null</code> to leave not set
     * @param id optional MX identification for the MsgDefIdr (message definition identifier) element or <code>null</code> to leave not set
     * @return new header initialized from parameters.
     * @since 7.8.5
     */
    public static BusinessHeader create(final String sender, final String receiver, final String reference,
            final MxId id) {
        BusinessHeader h = new BusinessHeader();
        h.setBusinessApplicationHeader(new BusinessApplicationHeaderV01());

        if (sender != null) {
            h.getBusinessApplicationHeader().setFr(new Party9Choice());
            h.getBusinessApplicationHeader().getFr().setFIId(new BranchAndFinancialInstitutionIdentification5());
            h.getBusinessApplicationHeader().getFr().getFIId()
                    .setFinInstnId(new FinancialInstitutionIdentification8());
            h.getBusinessApplicationHeader().getFr().getFIId().getFinInstnId().setBICFI(sender);
        }

        if (receiver != null) {
            h.getBusinessApplicationHeader().setTo(new Party9Choice());
            h.getBusinessApplicationHeader().getTo().setFIId(new BranchAndFinancialInstitutionIdentification5());
            h.getBusinessApplicationHeader().getTo().getFIId()
                    .setFinInstnId(new FinancialInstitutionIdentification8());
            h.getBusinessApplicationHeader().getTo().getFIId().getFinInstnId().setBICFI(receiver);
        }

        if (reference != null) {
            h.getBusinessApplicationHeader().setBizMsgIdr(reference);
        }

        if (id != null) {
            h.getBusinessApplicationHeader().setMsgDefIdr(id.id());
        }

        h.getBusinessApplicationHeader().setCreDt(now());

        return h;
    }

    /**
     * Sets the creation date in the inner header object with current moment in UTC time zone.
     * <br>
     * Either of the inner headers must be not null. If both are null this method does nothing.
     * @param overwrite if true, the creation date will always be set overwriting any previous value; if false it will be set only if it is not already set 
     * @since 7.8.5
     */
    public void setCreationDate(boolean overwrite) {
        if (this.businessApplicationHeader != null
                && (this.businessApplicationHeader.getCreDt() == null || overwrite)) {
            this.businessApplicationHeader.setCreDt(now());
        } else if (this.applicationHeader != null && (this.applicationHeader.getCrDate() == null || overwrite)) {
            this.applicationHeader.setCrDate(now());
        }
    }

    /**
     * Returns a gregorian calendar for current moment in UTC time zone
     * @return created calendar or null if DatatypeFactory fails to create the calendar instance
     */
    private static XMLGregorianCalendar now() {
        GregorianCalendar c = new GregorianCalendar();
        c.setTime(Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime());
        XMLGregorianCalendar creationDate = null;
        try {
            /*
             * important: cannot create XMLGregorianCalendar directly from Calendar object, 
             * specific format must be used for the unmarshalled XML to pass XSD validation.
             */
            creationDate = DatatypeFactory.newInstance()
                    .newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(c.getTime()));
        } catch (DatatypeConfigurationException e) {
            log.log(Level.WARNING, "error initializing header creation date", e);
        }
        return creationDate;
    }
}