com.zimbra.cs.client.soap.LmcSoapRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.cs.client.soap.LmcSoapRequest.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * version 2 of the License.
 *
 * 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.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

package com.zimbra.cs.client.soap;

import com.zimbra.cs.client.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;

import com.zimbra.common.util.Log;
import com.zimbra.common.util.LogFactory;
import org.dom4j.Element;

import com.zimbra.common.auth.ZAuthToken;
import com.zimbra.common.soap.DomUtil;
import com.zimbra.common.soap.SoapFaultException;
import com.zimbra.common.soap.SoapHttpTransport;
import com.zimbra.common.soap.SoapParseException;
import com.zimbra.common.service.ServiceException;
import com.zimbra.common.util.StringUtil;
import com.zimbra.common.soap.MailConstants;
import com.zimbra.common.soap.AdminConstants;

public abstract class LmcSoapRequest {

    private static Log sLog = LogFactory.getLog(LmcSoapRequest.class);

    private static boolean sDumpXML = false;

    public static synchronized void setDumpXML(boolean b) {
        sDumpXML = b;
    }

    private static int sRetryCount = -1;

    public static synchronized void setRetryCount(int n) {
        sRetryCount = n;
    }

    private static int sTimeoutMillis = -1;

    public static synchronized void setTimeout(int millis) {
        sTimeoutMillis = millis;
    }

    /*
    * If session is null, no auth information will be sent.  Otherwise the 
    * auth information in session will be sent.
    */
    protected LmcSession mSession;

    public LmcSession getSession() {
        return mSession;
    }

    public void setSession(LmcSession l) {
        mSession = l;
    }

    /*
      * If requestedAccountId is not null, the request is sent on behalf of
      * the account.
      */
    protected String mRequestedAccountId;

    public String getRequestedAccountId() {
        return mRequestedAccountId;
    }

    public void setRequestedAccountId(String id) {
        mRequestedAccountId = id;
    }

    /**
     * A particular type of request must implement this to return the XML
     * that should be sent in the soap:body element.
     * XXX: this will do no validation.  may want a boolean to do validation
     * to make it more general purpose.  don't want validation as a test
     * client since you want to be able to simulate errors.
     */
    protected abstract Element getRequestXML() throws LmcSoapClientException;

    /**
     * A particular type of request must implement this to take the XML
     * returned from the server, parse it, and create a SoapResponse
     * object from it.
     * @param responseXML - the element that is the root of the soap:Body
     * element that contains the server's response.
     * @exception SoapParseException if the parser cannot find elements
     * that are required in the response.
     */
    protected abstract LmcSoapResponse parseResponseXML(Element responseXML)
            throws SoapParseException, ServiceException, LmcSoapClientException;

    /**
     * After setting up all the request parameters, send this request to
     * the targetURL and return the server's response.
     * This method will side-effect the LmcSession object assigned with
     * setSession, changing the session ID if the server changed it.
     * @param targetURL - the URL of the SOAP service to send the request to
     * @exception lotsOfthem
     */
    public LmcSoapResponse invoke(String targetURL)
            throws LmcSoapClientException, IOException, SoapFaultException, ServiceException, SoapParseException {
        LmcSoapResponse result = null;

        SoapHttpTransport trans = null;
        try {
            trans = new SoapHttpTransport(targetURL);
            trans.setTimeout(sTimeoutMillis);
            trans.setRetryCount(sRetryCount);
            trans.setUserAgent("lmc", null);

            // set the auth token and session id in the transport for this request to use
            String curSessionID = null;
            if (mSession != null) {
                ZAuthToken zat = mSession.getAuthToken();
                trans.setAuthToken(zat);
                curSessionID = mSession.getSessionID();
                trans.setSessionId(curSessionID);
            }

            // send it over
            Element requestXML = getRequestXML();
            if (sDumpXML) {
                sLog.info("Request:" + DomUtil.toString(requestXML, true));
            }
            com.zimbra.common.soap.Element requestElt = com.zimbra.common.soap.Element.convertDOM(requestXML);
            //System.out.println("Sending over request " + DomUtil.toString(requestXML, true));
            Element responseXML;
            if (mRequestedAccountId == null)
                responseXML = trans.invoke(requestElt).toXML();
            else
                responseXML = trans.invoke(requestElt, false, true, mRequestedAccountId).toXML();
            if (sDumpXML) {
                sLog.info("Response:" + DomUtil.toString(responseXML, true) + "\n");
            }

            /*
            * check to see if the session ID changed.  this will not change
            * the session ID if there was one and trans.getSessionId() now
            * returns null.
            */
            String newSessionID = trans.getSessionId();
            if (newSessionID != null && mSession != null) {
                if (curSessionID == null)
                    mSession.setSessionID(newSessionID);
                else if (!curSessionID.equals(newSessionID))
                    mSession.setSessionID(newSessionID);
            }

            // parse the response
            //System.out.println(DomUtil.toString(responseXML, true));
            result = parseResponseXML(responseXML);

        } finally {
            if (trans != null)
                trans.shutdown();
        }
        return result;
    }

    protected LmcContact[] parseContactArray(Element parentElem) throws ServiceException {
        // iterate over all the <cn> elements in parentElem
        ArrayList contactArray = new ArrayList();
        for (Iterator ait = parentElem.elementIterator(MailConstants.E_CONTACT); ait.hasNext();) {
            Element a = (Element) ait.next();
            contactArray.add(parseContact(a));
        }

        if (contactArray.isEmpty()) {
            return null;
        } else {
            LmcContact contacts[] = new LmcContact[contactArray.size()];
            return (LmcContact[]) contactArray.toArray(contacts);
        }
    }

    protected static void addAttrNotNull(Element elem, String attrName, String attrValue) {
        if (attrValue != null)
            DomUtil.addAttr(elem, attrName, attrValue);
    }

    protected void addContactAttr(Element parent, LmcContactAttr attr) {
        String attrValue = attr.getAttrData();
        Element newAttr = DomUtil.add(parent, MailConstants.E_ATTRIBUTE, (attrValue == null) ? "" : attrValue);
        DomUtil.addAttr(newAttr, MailConstants.A_ATTRIBUTE_NAME, attr.getAttrName());
    }

    protected LmcConversation parseConversation(Element conv) throws LmcSoapClientException, ServiceException {
        LmcConversation result = new LmcConversation();

        // get the conversation attributes
        result.setID(conv.attributeValue(MailConstants.A_ID));
        result.setTags(conv.attributeValue(MailConstants.A_TAGS));
        String numMsgs = conv.attributeValue(MailConstants.A_NUM);
        if (numMsgs != null)
            result.setNumMessages(Integer.parseInt(numMsgs));
        result.setDate(conv.attributeValue(MailConstants.A_DATE));
        result.setFlags(conv.attributeValue(MailConstants.A_FLAGS));
        result.setFolder(conv.attributeValue(MailConstants.A_FOLDER));

        /*
           * iterate over subelements. allowed subelements are e-mail addresses,
           * subject, fragment, and messages. this assumes no particular order is
           * required for correctness.
           */
        ArrayList emailAddrs = new ArrayList();
        ArrayList msgs = new ArrayList();
        for (Iterator it = conv.elementIterator(); it.hasNext();) {
            Element e = (Element) it.next();

            // find out what element it is and go process that
            String elementType = e.getQName().getName();
            if (elementType.equals(MailConstants.E_FRAG)) {
                // fragment
                result.setFragment(e.getText());
            } else if (elementType.equals(MailConstants.E_EMAIL)) {
                // e-mail address
                LmcEmailAddress ea = parseEmailAddress(e);
                emailAddrs.add(ea);
            } else if (elementType.equals(MailConstants.E_SUBJECT)) {
                // subject
                result.setSubject(e.getText());
            } else if (elementType.equals(MailConstants.E_MSG)) {
                // message
                LmcMessage m = parseMessage(e);
                msgs.add(m);
            } else {
                // don't know what it is
                throw new LmcSoapClientException("unknown element " + elementType + " within conversation");
            }
        }

        // set the arrays in the result object
        if (!emailAddrs.isEmpty()) {
            LmcEmailAddress a[] = new LmcEmailAddress[emailAddrs.size()];
            result.setParticipants((LmcEmailAddress[]) emailAddrs.toArray(a));
        }
        if (!msgs.isEmpty()) {
            LmcMessage m[] = new LmcMessage[msgs.size()];
            result.setMessages((LmcMessage[]) msgs.toArray(m));
        }
        return result;
    }

    protected LmcContactAttr parseContactAttr(Element cna) throws ServiceException {
        // get the attributes
        String attrName = DomUtil.getAttr(cna, MailConstants.A_ATTRIBUTE_NAME);
        String attrID = cna.attributeValue(MailConstants.A_ID);
        String ref = cna.attributeValue(MailConstants.A_REF);
        String attrData = cna.getText();

        LmcContactAttr lca = new LmcContactAttr(attrName, attrID, ref, attrData);
        return lca;
    }

    protected LmcContact parseContact(Element cn) throws ServiceException {
        LmcContact result = new LmcContact();

        // get the element's attributes
        result.setID(DomUtil.getAttr(cn, MailConstants.A_ID));
        result.setTags(cn.attributeValue(MailConstants.A_TAGS));
        result.setFlags(cn.attributeValue(MailConstants.A_FLAGS));
        result.setFolder(cn.attributeValue(MailConstants.A_FOLDER));

        // get the contact attributes (<a> elements)
        ArrayList cnAttrs = new ArrayList();
        for (Iterator ait = cn.elementIterator(MailConstants.E_ATTRIBUTE); ait.hasNext();) {
            Element cnAttrElem = (Element) ait.next();
            cnAttrs.add(parseContactAttr(cnAttrElem));
        }
        if (!cnAttrs.isEmpty()) {
            LmcContactAttr cnAttrArray[] = new LmcContactAttr[cnAttrs.size()];
            result.setAttrs((LmcContactAttr[]) cnAttrs.toArray(cnAttrArray));
        }

        // XXX: not clear from spec if the <mp> element is used -- assume not 
        return result;
    }

    protected LmcMessage parseMessage(Element msg) throws ServiceException, LmcSoapClientException {
        LmcMessage result = new LmcMessage();

        // get the message attributes
        result.setID(DomUtil.getAttr(msg, MailConstants.A_ID));
        result.setFlags(msg.attributeValue(MailConstants.A_FLAGS));
        String size = msg.attributeValue(MailConstants.A_SIZE);
        if (size != null)
            result.setSize(Integer.parseInt(size));
        result.setContentMatched(msg.attributeValue(MailConstants.A_CONTENTMATCHED));
        result.setDate(msg.attributeValue(MailConstants.A_DATE));
        result.setConvID(msg.attributeValue(MailConstants.A_CONV_ID));
        result.setFolder(msg.attributeValue(MailConstants.A_FOLDER));
        result.setOriginalID(msg.attributeValue(MailConstants.A_ORIG_ID));

        /*
           * iterate over subelements. allowed subelements are content (at most
           * 1), subject, fragment, and msg parts. this assumes no particular
           * order is required for correctness.
           */
        ArrayList emailAddrs = new ArrayList();
        for (Iterator it = msg.elementIterator(); it.hasNext();) {
            Element e = (Element) it.next();

            // find out what element it is and go process that
            String elementType = e.getQName().getName();
            if (elementType.equals(MailConstants.E_FRAG)) {
                // fragment
                result.setFragment(e.getText());
            } else if (elementType.equals(MailConstants.E_EMAIL)) {
                // e-mail address
                LmcEmailAddress ea = parseEmailAddress(e);
                emailAddrs.add(ea);
            } else if (elementType.equals(MailConstants.E_SUBJECT)) {
                // subject
                result.setSubject(e.getText());
            } else if (elementType.equals(MailConstants.E_MIMEPART)) {
                // MIME part
                LmcMimePart mp = parseMimePart(e);
                result.addMimePart(mp);
            } else if (elementType.equals(MailConstants.E_MSG_ID_HDR)) {
                // message ID header
                result.setMsgIDHeader(e.getText());
            } else if (elementType.equals(MailConstants.E_INVITE)) {
                // ignore appointment invites for now
            } else {
                // don't know what it is
                throw new LmcSoapClientException("unknown element " + elementType + " within message");
            }
        }

        if (!emailAddrs.isEmpty()) {
            LmcEmailAddress a[] = new LmcEmailAddress[emailAddrs.size()];
            result.setEmailAddresses((LmcEmailAddress[]) emailAddrs.toArray(a));
        }

        return result;
    }

    protected LmcMimePart parseMimePart(Element mp) throws LmcSoapClientException, ServiceException {
        LmcMimePart result = new LmcMimePart();

        // get the attributes
        result.setPartName(DomUtil.getAttr(mp, MailConstants.A_PART));
        result.setIsBody(mp.attributeValue(MailConstants.A_BODY));
        result.setSize(mp.attributeValue(MailConstants.A_SIZE));
        result.setMessageID(mp.attributeValue(MailConstants.A_MESSAGE_ID));
        result.setConvID(mp.attributeValue(MailConstants.A_CONV_ID));
        result.setContentType(mp.attributeValue(MailConstants.A_CONTENT_TYPE));
        result.setContentTypeName(mp.attributeValue(MailConstants.A_CONTENT_NAME));
        result.setContentDisp(mp.attributeValue(MailConstants.A_CONTENT_DISPOSITION));
        result.setContentDispFilename(mp.attributeValue(MailConstants.A_CONTENT_FILENAME));
        // XXX assume that content description is an attr of <mp> and not <content>
        result.setContentDesc(mp.attributeValue(MailConstants.A_CONTENT_DESCRIPTION));

        // parse any interior elements (content or another MIME part)
        ArrayList subMimeParts = new ArrayList();
        for (Iterator it = mp.elementIterator(); it.hasNext();) {
            Element e = (Element) it.next();

            // find out what element it is and go process that
            String elementType = e.getQName().getName();
            if (elementType.equals(MailConstants.E_CONTENT)) {
                addContent(result, e);
            } else if (elementType.equals(MailConstants.E_MIMEPART)) {
                LmcMimePart nextPart = parseMimePart(e);
                subMimeParts.add(nextPart);
            } else {
                // unexpected element
                throw new LmcSoapClientException("unexpected element " + elementType);
            }
        }

        if (!subMimeParts.isEmpty()) {
            LmcMimePart mpArr[] = new LmcMimePart[subMimeParts.size()];
            result.setSubParts((LmcMimePart[]) subMimeParts.toArray(mpArr));
        }
        return result;
    }

    /**
     * Add the information in the Element c to the MimePart mp.
     * @param mp
     * @param c
     */
    protected void addContent(LmcMimePart mp, Element c) {
        // XXX need constant
        mp.setContentEncoding(c.attributeValue("cte"));
        mp.setContent(c.getText());
    }

    protected LmcEmailAddress parseEmailAddress(Element ea) {
        LmcEmailAddress result = new LmcEmailAddress();

        // grab all the attributes
        result.setType(ea.attributeValue(MailConstants.A_ADDRESS_TYPE));
        result.setEmailID(ea.attributeValue(MailConstants.A_ID));
        result.setReferencedID(ea.attributeValue(MailConstants.A_REF));
        result.setPersonalName(ea.attributeValue(MailConstants.A_PERSONAL));
        result.setEmailAddress(ea.attributeValue(MailConstants.A_ADDRESS));
        result.setDisplayName(ea.attributeValue(MailConstants.A_DISPLAY));

        // get the content if any
        result.setContent(ea.getText());

        return result;
    }

    /**
     * This parses the <folder> element pointed to by p.
     * @param p - the folder element
     * @return an LmcFolder object that is populated with data from the XML
     * @throws ServiceException
     */
    protected LmcFolder parseFolder(Element f) throws ServiceException {
        LmcFolder response = new LmcFolder();

        /*
        * Get the attributes of this folder element.  The root folder
        * object does not have the name, parent, and numUnread attributes.
        * Hence this parses them as optional.
        */
        response.setFolderID(DomUtil.getAttr(f, MailConstants.A_ID));
        response.setName(f.attributeValue(MailConstants.A_NAME));
        response.setParentID(f.attributeValue(MailConstants.A_FOLDER));
        response.setNumUnread(f.attributeValue(MailConstants.A_UNREAD));
        response.setView(f.attributeValue(MailConstants.A_DEFAULT_VIEW));

        // recurse if necessary
        ArrayList subFolders = new ArrayList();
        for (Iterator ait = f.elementIterator(MailConstants.E_FOLDER); ait.hasNext();) {
            Element sub = (Element) ait.next();
            subFolders.add(parseFolder(sub));
        }
        if (!subFolders.isEmpty()) {
            LmcFolder fs[] = new LmcFolder[subFolders.size()];
            response.setSubFolders((LmcFolder[]) subFolders.toArray(fs));
        }
        return response;
    }

    protected void addPrefToMultiMap(HashMap prefMap, Element e) throws ServiceException {
        String name = DomUtil.getAttr(e, AdminConstants.A_NAME);
        String value = e.getText();
        StringUtil.addToMultiMap(prefMap, name, value);
    }

    protected LmcNote parseNote(Element n) throws ServiceException {
        LmcNote result = new LmcNote();

        // grab all the attributes
        result.setID(DomUtil.getAttr(n, MailConstants.A_ID));
        result.setTags(n.attributeValue(MailConstants.A_TAGS));
        result.setDate(DomUtil.getAttr(n, MailConstants.A_DATE));
        result.setFolder(DomUtil.getAttr(n, MailConstants.A_FOLDER));
        result.setPosition(DomUtil.getAttr(n, MailConstants.A_BOUNDS));
        result.setColor(DomUtil.getAttr(n, MailConstants.A_COLOR));

        // get the content
        Element c = DomUtil.get(n, MailConstants.E_CONTENT);
        result.setContent(c.getText());

        return result;
    }

    protected LmcTag parseTag(Element t) throws ServiceException {
        String name = DomUtil.getAttr(t, MailConstants.A_NAME);
        String id = DomUtil.getAttr(t, MailConstants.A_ID);
        String color = DomUtil.getAttr(t, MailConstants.A_COLOR);
        long unreadCount = DomUtil.getAttrLong(t, MailConstants.A_UNREAD, -1);
        LmcTag newTag = (unreadCount == -1) ? new LmcTag(id, name, color)
                : new LmcTag(id, name, color, unreadCount);
        return newTag;
    }

    protected LmcDocument parseDocument(Element doc) {
        LmcDocument result = parseDocumentCommon(doc, new LmcDocument());
        return result;
    }

    protected LmcWiki parseWiki(Element wiki) {
        LmcWiki result = new LmcWiki();
        parseDocumentCommon(wiki, result);

        result.setWikiWord(wiki.attributeValue(MailConstants.A_NAME));

        try {
            Element c = DomUtil.get(wiki, MailConstants.A_BODY);
            result.setContents(c.getText());
        } catch (Exception e) {
        }

        return result;
    }

    private LmcDocument parseDocumentCommon(Element doc, LmcDocument result) {
        result.setID(doc.attributeValue(MailConstants.A_ID));
        result.setName(doc.attributeValue(MailConstants.A_NAME));
        result.setContentType(doc.attributeValue(MailConstants.A_CONTENT_TYPE));
        result.setFolder(doc.attributeValue(MailConstants.A_FOLDER));
        result.setRev(doc.attributeValue(MailConstants.A_VERSION));
        result.setLastModifiedDate(doc.attributeValue(MailConstants.A_DATE));
        result.setLastEditor(doc.attributeValue(MailConstants.A_LAST_EDITED_BY));
        result.setRestUrl(doc.attributeValue(MailConstants.A_REST_URL));
        result.setCreator(doc.attributeValue(MailConstants.A_CREATOR));
        result.setCreateDate(doc.attributeValue(MailConstants.A_CREATED_DATE));

        for (Iterator it = doc.elementIterator(); it.hasNext();) {
            Element e = (Element) it.next();

            String elementType = e.getQName().getName();
            if (elementType.equals(MailConstants.E_FRAG)) {
                // fragment
                result.setFragment(e.getText());
            }
        }
        return result;
    }

    /**
     * Add the XML representation of the message to the element.
     * @param e - the element at the root
     * @param msg - the msg to be represented as XML
     */
    protected void addMsg(Element e, LmcMessage msg, String inReplyTo, String fwdMsgID, String[] fwdPartNumbers) {
        Element m = DomUtil.add(e, MailConstants.E_MSG, "");

        // attributes on the message element
        addAttrNotNull(m, MailConstants.A_ORIG_ID, msg.getOriginalID());
        addAttrNotNull(m, MailConstants.A_FOLDER, msg.getFolder());
        addAttrNotNull(m, MailConstants.A_TAG, msg.getTag());

        // for all e-mail addresses, add them
        LmcEmailAddress addrs[] = msg.getEmailAddresses();
        for (int i = 0; addrs != null && i < addrs.length; i++)
            addEmailAddress(m, addrs[i]);

        // add subject
        DomUtil.add(m, MailConstants.E_SUBJECT, msg.getSubject());

        // content if present
        String content = msg.getContent();
        if (content != null)
            DomUtil.add(m, MailConstants.E_CONTENT, content);

        // In-Reply-To header if present
        if (inReplyTo != null)
            DomUtil.add(m, MailConstants.E_IN_REPLY_TO, inReplyTo);

        // message parts
        LmcMimePart mp = msg.getMimePart(0);
        if (mp != null)
            addMimePart(m, mp);

        // attachment ID's if present
        String attachmentIDs[] = msg.getAttachmentIDs();
        if (attachmentIDs != null) {
            for (int i = 0; i < attachmentIDs.length; i++) {
                Element aid = DomUtil.add(m, MailConstants.E_ATTACH, "");
                addAttrNotNull(aid, MailConstants.A_ATTACHMENT_ID, attachmentIDs[i]);
            }
        }

        // forwarding messages with attachments
        if (fwdPartNumbers != null) {
            Element attach = DomUtil.add(m, MailConstants.E_ATTACH, "");
            for (int i = 0; i < fwdPartNumbers.length; i++) {
                Element part = DomUtil.add(attach, MailConstants.E_MIMEPART, "");
                DomUtil.addAttr(part, MailConstants.A_MESSAGE_ID, fwdMsgID);
                DomUtil.addAttr(part, MailConstants.A_PART, fwdPartNumbers[i]);
            }
        }
    }

    /**
     * Add the XML representation of the MIME part to the element.
     * @param m - the element
     * @param mp - the MIME part to be added
     */
    protected void addMimePart(Element m, LmcMimePart mp) {
        Element mpElem = DomUtil.add(m, MailConstants.E_MIMEPART, "");
        addAttrNotNull(mpElem, MailConstants.A_PART, mp.getPartName());
        addAttrNotNull(mpElem, MailConstants.A_BODY, mp.getIsBody());
        addAttrNotNull(mpElem, MailConstants.A_SIZE, mp.getSize());
        addAttrNotNull(mpElem, MailConstants.A_MESSAGE_ID, mp.getMessageID());
        addAttrNotNull(mpElem, MailConstants.A_CONV_ID, mp.getConvID());
        addAttrNotNull(mpElem, MailConstants.A_CONTENT_TYPE, mp.getContentType());
        addAttrNotNull(mpElem, MailConstants.A_CONTENT_NAME, mp.getContentTypeName());
        addAttrNotNull(mpElem, MailConstants.A_CONTENT_DISPOSITION, mp.getContentDisp());
        addAttrNotNull(mpElem, "filename", mp.getContentDispFilename()); // XXX: need constant

        // add the content element if present
        String content = mp.getContent();
        if (content != null) {
            Element cElem = DomUtil.add(mpElem, MailConstants.E_CONTENT, content);
            addAttrNotNull(cElem, "cte", mp.getContentEncoding()); // XXX: need constant
        }

        // add all subparts 
        LmcMimePart subParts[] = mp.getSubParts();
        for (int i = 0; subParts != null && i < subParts.length; i++)
            addMimePart(mpElem, subParts[i]);
    }

    /**
     * Add the XML representation of addr on to the element m
     */
    protected void addEmailAddress(Element m, LmcEmailAddress addr) {
        String content = addr.getContent();
        Element e = DomUtil.add(m, MailConstants.E_EMAIL, (content == null) ? "" : content);
        addAttrNotNull(e, MailConstants.A_ADDRESS_TYPE, addr.getType());
        addAttrNotNull(e, MailConstants.A_ADDRESS, addr.getEmailAddress());
        addAttrNotNull(e, MailConstants.A_PERSONAL, addr.getPersonalName());
    }
}