com.funambol.LDAP.dao.impl.ContactDAO.java Source code

Java tutorial

Introduction

Here is the source code for com.funambol.LDAP.dao.impl.ContactDAO.java

Source

/**
 *
 * 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; either version 2 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.  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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

package com.funambol.LDAP.dao.impl;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;

import org.apache.commons.lang.StringUtils;

import com.funambol.LDAP.dao.ContactDAOInterface;
import com.funambol.LDAP.utils.Constants;
import com.funambol.LDAP.utils.LdapUtils;
import com.funambol.common.pim.common.Property;
import com.funambol.common.pim.contact.Address;
import com.funambol.common.pim.contact.BusinessDetail;
import com.funambol.common.pim.contact.Contact;
import com.funambol.common.pim.contact.Email;
import com.funambol.common.pim.contact.Name;
import com.funambol.common.pim.contact.Note;
import com.funambol.common.pim.contact.PersonalDetail;
import com.funambol.common.pim.contact.Phone;
import com.funambol.common.pim.contact.Title;
import com.funambol.common.pim.vcard.ParseException;
import com.funambol.common.pim.vcard.VcardParser;
import com.funambol.framework.engine.SyncItem;
import com.funambol.framework.engine.SyncItemState;
import com.funambol.framework.engine.source.SyncSourceException;
import com.funambol.framework.logging.FunambolLogger;
import com.funambol.framework.logging.FunambolLoggerFactory;
import com.funambol.server.config.Configuration;

/**
 * This class implements the Data Access Object for <i>Contacts</i>. Based on a
 * LDAP entry this class creates a <i>Contact</i> object
 * (sync4j.foundation.pdi.contact.Contact). The <i>Contact</i> object allows to
 * be transformed to a vcard of a SIFC.
 * 
 * TODO support vcard3.0, support for entryKey
 * 
 * @author <a href='mailto:fipsfuchs _@ users.sf.net'>Philipp Kamps</a>
 * @author <a href='mailto:julien.buratto _@ subitosms.it'>Julien Buratto</a>
 * @author <a href='mailto:gdartigu _@ smartjog.com'>Gilles Dartiguelongue</a>
 * @author <a href='mailto:pventura _@ babel.it>Pietro Ventura</a>
 * @author <a hreg='mailto:rpolli _@ babel.it'>Roberto Polli</a>
 * 
 * @version $Id$
 */
public class ContactDAO implements ContactDAOInterface {
    // ----------------------------------------------------------- Private data
    public String SUPPORTED_ATTRIBUTES[] = { "givenName", "sn", "cn", "telephoneNumber", "description", "title",
            "facsimileTelephoneNumber", "postalCode", "postalAddress", "st", "l", "homePhone", "mobile",
            "homePostalAddress", "initials", "mail", "street", "modifyTimestamp" };

    // PV
    // constants for modification attributesets
    public static final String ADD_ATTRIBUTE = "ADD";
    public static final String DEL_ATTRIBUTE = "DEL";
    public static final String REPLACE_ATTRIBUTE = "REPLACE";

    protected FunambolLogger logger = FunambolLoggerFactory.getLogger(Constants.LOGGER_LDAP_DAO);

    protected String rdnAttribute = "cn";

    // ------------------------------------------------------------ Constructor

    /**
     * The constructor is called the the LDAPInterface object and it stores this
     * object as a private object variable This class works on all fields except
     * of the LDAPID, which is managed directly into the LDAPInterface
     * 
     * @param ldapInterface
     *            Referenz to the LDAP Interface to read single LDAP entries
     */
    public ContactDAO() {

    }

    // --------------------------------------------------------- Public methods

    /**
     * Call this method with a given array of ids for LDAP entries and it will
     * return a corresponding <i>Contact[]</i> array.
     * 
     * @param ids
     *            lists of IDs
     * @return list of contacts corresponding to the provided IDs
     * @deprecated this shouldn't access ldap
     */
    // public List<Contact> getContacts( List<String> ids ){
    // List<Contact> result = new ArrayList<Contact>();
    //
    // Iterator<String> it = ids.iterator();
    //
    // while(it.hasNext()) {
    // result.add( getContact( it.next() ));
    // }
    //
    // return result;
    // }
    /**
     * Call this method with a given id for a LDAP entry and it will return a
     * corresponding <i>Contact</i> object.
     * 
     * @param id
     *            id of the LDAP entry
     * @return the contact corresponding to the id
     * @deprecated this class shouldn't access ldap TODO this method makes an
     *             LDAPSearch
     */

    // private Contact getContact( String id )
    // {
    // Attributes entry = li.getLDAPEntryById(id).getAttributes();
    //
    // if (entry == null) {
    // logger.warn("Unable to get the LDAP entry for id: " + id);
    // return null;
    // }
    //
    // return createContact(entry);
    // }
    /**
     * Call this method with a given <i>SyncItem</i> to get the equivalent
     * attributes to inject in LDAP
     * 
     * @param si
     *            <i>SyncItem</i> to convert to LDAP attributes
     * @return LDAP attributes representation of the <i>SyncItem</i>
     */
    public Attributes syncItemToLdapAttributes(SyncItem si) {
        Contact contact = getContact(si);

        if (contact == null) {
            logger.info("Unexpected error while retrieving contact from SyncItem");
            return null;
        }

        return createEntry(contact);
    }

    /**
     * Call this method with a given <i>SyncItem</i>, it will return a
     * corresponding <i>Contact</i> object.
     * 
     * @param syncItem
     *            <i>SyncItem</i> to get the <i>Contact</i> from
     * @return the contact corresponding to the <i>SyncItem</i>
     * @throws SyncSourceException
     *             FIXME rename this function with a more explicit name (ex.
     *             syncItemToContact, convertItem, ..)
     */
    protected Contact getContact(SyncItem syncItem) {

        if (!Arrays.asList(Constants.SUPPORTED_TYPES).contains(syncItem.getType())) {
            logger.warn("Error parsing the item " + syncItem.getKey().getKeyAsString()
                    + " from vCard to a Contact object of type: " + syncItem.getType());

            if (Configuration.getConfiguration().isDebugMode() && logger.isTraceEnabled()) {
                logger.trace("Item content follows: " + (new String(syncItem.getContent())));
            }
            return null;
        }
        byte[] content = syncItem.getContent();
        VcardParser parser = new VcardParser(new ByteArrayInputStream(content));
        Contact contact;

        try {
            // Convert the SyncItem to Contact
            contact = (Contact) parser.vCard();

        } catch (ParseException e) {
            logger.warn("Error parsing the item " + syncItem.getKey().getKeyAsString()
                    + " from vCard to a Contact object: " + e.getMessage());

            if (Configuration.getConfiguration().isDebugMode() && logger.isTraceEnabled()) {
                logger.trace(" vCard follows: " + (new String(content)));
            }
            return null;
        }

        if (syncItem.getState() == SyncItemState.UPDATED) {
            contact.setUid((String) syncItem.getKey().getKeyValue());
        }
        if (Configuration.getConfiguration().isDebugMode() && logger.isTraceEnabled()) {
            logger.trace("Original content");
            logger.trace("----------ORIGINAL------");
            logger.trace(content.toString());

            for (int i = 0; i < content.length; i++)
                logger.trace("" + (char) (content[i]));

            logger.trace("Converted SyncItem to Contact");
            logger.trace("----------ORIGINAL------");
            logger.trace(new String(content));
        }
        return contact;
    }

    // ------------------------------------------------------ Protected methods

    /**
     * Here's the place where all the magic happens: This method transforms a
     * LDAP entry to a <i>Contact</i> object.
     * 
     * @param entry
     *            The LDAP entry
     * @return contact from the LDAP server
     */
    public Contact createContact(Attributes attrs) {

        if (attrs == null) {
            logger.warn("Entry from LDAP is null so won't be able to create a contact");
            return null;
        }
        try {
            if (logger.isDebugEnabled())
                logger.debug("Getting attribute cn");

            String cn = (attrs.get("cn") != null) ? (String) attrs.get("cn").get() : "null";
            // logger.info("Getting attribute " + ldapId);
            // String entryId = ( attrs.get(ldapId) != null ) ? (String)
            // attrs.get(ldapId).get() : "null";

            if (logger.isDebugEnabled()) {
                logger.debug("Creating Contact for " + cn
                // + ". Id is ( "+ entryId + " )"
                );
            }
            Contact contact = new Contact();

            /* Set name */
            Name name = new Name();
            if (attrs.get("cn") != null) {
                name.getDisplayName().setPropertyValue(cn);
            }
            if (attrs.get("givenName") != null) {
                name.getFirstName().setPropertyValue((String) attrs.get("givenName").get());
            }
            if (attrs.get("sn") != null) {
                name.getLastName().setPropertyValue((String) attrs.get("sn").get());
            }
            if (attrs.get("middleName") != null) {
                name.getMiddleName().setPropertyValue((String) attrs.get("middleName").get());
            }
            if (attrs.get("title") != null) {
                name.getSalutation().setPropertyValue((String) attrs.get("title").get());
            }
            if (attrs.get("nickName") != null) {
                name.getNickname().setPropertyValue((String) attrs.get("nickName").get());
            }
            contact.setName(name);

            /* Set personal details */
            PersonalDetail personal = new PersonalDetail();
            BusinessDetail business = new BusinessDetail();

            /* Set email */
            if (attrs.get("mail") != null) {
                Email email = new Email();
                email.setEmailType("Email1Address");
                email.setPropertyValue((String) attrs.get("mail").get());
                personal.addEmail(email);
            }
            if (attrs.get("mailAlternateAddress") != null) {
                Email email = new Email();
                email.setEmailType("Email2Address");
                email.setPropertyValue((String) attrs.get("mailAlternateAddress").get());
                personal.addEmail(email);
            }

            // (Other, Home, Business) x (Telephone,Mobile) x Number

            /* Set phone phones */
            ArrayList<Phone> allPhones = new ArrayList<Phone>();

            // telephone: home, work,
            if (attrs.get("homePhone") != null) {
                Phone homePhone = new Phone();
                homePhone.setPropertyValue((String) attrs.get("homePhone").get());
                homePhone.setPhoneType("HomeTelephoneNumber");
                allPhones.add(homePhone);
            }

            if (attrs.get("mobile") != null) {
                Phone mobile = new Phone();
                mobile.setPropertyValue((String) attrs.get("mobile").get());
                mobile.setPhoneType("MobileTelephoneNumber");
                allPhones.add(mobile);
            }

            String val = LdapUtils.getPrintableAttribute(attrs.get("telephoneNumber"));
            if (StringUtils.isNotEmpty(val)) {
                Phone phone = new Phone();
                phone.setPhoneType("BusinessTelephoneNumber");
                phone.setPropertyValue(val);
                business.addPhone(phone);
            }
            val = LdapUtils.getPrintableAttribute(attrs.get("facsimileTelephoneNumber"));
            if (StringUtils.isNotEmpty(val)) {
                Phone phone = new Phone();
                phone.setPhoneType("BusinessFaxNumber");
                phone.setPropertyValue(val);
                business.addPhone(phone);
            }

            personal.setPhones(allPhones);

            /*
            * Set address
            *//*
               * if(entry.getAttribute("postalCode")!= null) {
               * personal.getAddress().getPostalCode().setPropertyValue(
               * entry.getAttribute("postalCode").getStringValue()); }
               * 
               * if(entry.getAttribute("l")!= null) {
               * personal.getAddress().getCity().setPropertyValue(
               * entry.getAttribute("l").getStringValue()); }
               * 
               * if(entry.getAttribute("postalAddress")!= null) {
               * personal.getAddress().getStreet().setPropertyValue(
               * entry.getAttribute("postalAddress").getStringValue()); }
               */

            /* title */
            if (attrs.get("title") != null) {
                NamingEnumeration<?> values = attrs.get("title").getAll();
                ArrayList<Title> titles = new ArrayList<Title>();
                while (values.hasMoreElements()) {
                    titles.add(new Title((String) values.nextElement()));

                }
                business.setTitles(titles);
            }

            if (attrs.get("street") != null) {

                NamingEnumeration<?> streets = attrs.get("street").getAll();
                while (streets.hasMoreElements()) {
                    business.getAddress().getStreet().setPropertyValue((String) streets.nextElement());
                }

            }
            if (attrs.get("c") != null) {
                business.getAddress().getCountry().setPropertyValue((String) attrs.get("c").get());
            }

            if (attrs.get("l") != null) {
                business.getAddress().getCity().setPropertyValue((String) attrs.get("l").get());
            }

            if (attrs.get("postalCode") != null) {
                business.getAddress().getPostalCode().setPropertyValue((String) attrs.get("postalCode").get());
            }

            if (attrs.get("postalAddress") != null) {
                business.getAddress().getStreet().setPropertyValue((String) attrs.get("postalAddress").get());
            }

            if (attrs.get("o") != null) {
                business.getCompany().setPropertyValue((String) attrs.get("o").get());
            }
            if (attrs.get("calFbUrl") != null) {
                contact.setFreeBusy((String) attrs.get("calFbUrl").get());
            }
            contact.setPersonalDetail(personal);
            contact.setBusinessDetail(business);

            // contact.setUid(entryId);

            return contact;
        } catch (Exception e) {
            logger.warn("Error in getting entry values: ", e);
            return null;
        }
    }

    /**
     * Convert a <i>Contact</i> into a LDAP inetOrgPerson set of attributes.
     * This method is used in from Client to Server
     * 
     * @param contact
     *            contact to transform into Attributes
     * @return Attributes representation of the contact
     */
    public Attributes createEntry(Contact contact) {

        if (logger.isTraceEnabled())
            logger.trace("Working on contact:" + contact.getUid());

        Attributes attributeSet = new BasicAttributes();
        Attribute objClass = new BasicAttribute("objectClass");
        if (logger.isDebugEnabled())
            logger.debug("Ok let's add objectclass");

        objClass.add("inetOrgPerson");
        objClass.add("person");

        attributeSet.put(objClass);
        try {

            if (contact.getUid() == null) {
                contact.setUid(createUniqueId(contact));
                logger.info("UID is now: " + contact.getUid());
            }

            // Split contact object into sub-objects
            Name name = contact.getName();
            PersonalDetail personal = contact.getPersonalDetail();
            BusinessDetail business = contact.getBusinessDetail();

            List phones = personal.getPhones();
            List businessPhones = business.getPhones();

            List mails = personal.getEmails();
            List note = contact.getNotes();

            // personal address
            Address addr = personal.getAddress();

            // if displayname doesn't exist and the firstname and the lastname
            // are not both defined, this will result in a NullPointerException
            // I don't want to support any other ways of doing this right now.
            // a solution could be to use an UID for the rdn
            if (name != null) {

                if (propertyCheck(name.getLastName())) {
                    attributeSet.put(new BasicAttribute("sn", name.getLastName().getPropertyValueAsString()));
                } else {
                    attributeSet.put(new BasicAttribute("sn", ""));
                }

                if (propertyCheck(name.getFirstName())) {
                    attributeSet
                            .put(new BasicAttribute("givenName", name.getFirstName().getPropertyValueAsString()));
                } else {
                    attributeSet.put(new BasicAttribute("givenName", ""));
                }

                attributeSet.put(new BasicAttribute("cn", name.getFirstName().getPropertyValueAsString() + " "
                        + name.getLastName().getPropertyValueAsString()));
            }

            // Company name
            if (business != null && propertyCheck(business.getCompany())) {
                attributeSet.put(new BasicAttribute("o", business.getCompany().getPropertyValueAsString()));
            }

            // Adding phones
            if (phones != null && !phones.isEmpty()) {

                Iterator iter2 = phones.iterator();
                while (iter2.hasNext()) {
                    Phone phone = (Phone) iter2.next();

                    // if empty, no need to check type
                    if (!propertyCheck(phone))
                        continue;

                    // Home phones
                    if (phone.getPhoneType().equals("HomeTelephoneNumber")) {
                        attributeSet.put(new BasicAttribute("homePhone", phone.getPropertyValueAsString()));
                    }

                    // MobilePhones
                    if (phone.getPhoneType().equals("MobileTelephoneNumber"))
                        attributeSet.put(new BasicAttribute("mobile", phone.getPropertyValueAsString()));

                }
            }

            // Adding business phones
            if (businessPhones != null && !businessPhones.isEmpty()) {

                Iterator iter2 = businessPhones.iterator();
                while (iter2.hasNext()) {
                    Phone phone = (Phone) iter2.next();

                    // if empty, no need to check type
                    if (!propertyCheck(phone))
                        continue;

                    // Business phones
                    if (phone.getPhoneType().equals("BusinessTelephoneNumber")) {
                        attributeSet.put(new BasicAttribute("telephoneNumber", phone.getPropertyValueAsString()));
                    }
                    // Fax
                    if (phone.getPhoneType().equals("BusinessFaxNumber")) {
                        attributeSet.put(
                                new BasicAttribute("facsimiletelephonenumber", phone.getPropertyValueAsString()));
                    }
                }
            }

            if (mails != null && !mails.isEmpty()) {

                Iterator iter1 = mails.iterator();

                // For each email address, add it
                while (iter1.hasNext()) {
                    Email mail = (Email) iter1.next();
                    if (propertyCheck(mail))
                        attributeSet.put(new BasicAttribute("mail", mail.getPropertyValueAsString()));
                }
            }

            // Address
            if (addr != null) {
                if (propertyCheck(personal.getAddress().getPostalCode()))
                    attributeSet.put(new BasicAttribute("postalCode",
                            personal.getAddress().getPostalCode().getPropertyValueAsString()));

                if (propertyCheck(personal.getAddress().getStreet()))
                    attributeSet.put(new BasicAttribute("postalAddress",
                            personal.getAddress().getStreet().getPropertyValueAsString()));

                if (propertyCheck(personal.getAddress().getCity()))
                    attributeSet.put(
                            new BasicAttribute("l", personal.getAddress().getCity().getPropertyValueAsString()));
            }

            // Notes
            if (note != null && !note.isEmpty()) {
                Iterator note1 = note.iterator();
                while (note1.hasNext()) {
                    Note nota = (Note) note1.next();
                    if (propertyCheck(nota))
                        attributeSet.put(new BasicAttribute("description", nota.getPropertyValueAsString()));
                }
            }

            logger.info("Resulting LDAPAttributeSet is:");

            NamingEnumeration<String> ids = attributeSet.getIDs();

            while (ids.hasMoreElements()) {
                String attrID = ids.nextElement();
                logger.info(attrID + ": " + ((String) attributeSet.get(attrID).get()));

            }

            // Create the LDAPEntry with dn and attributes
            // THE DN is the DisplayName
            return attributeSet;

        } catch (Exception e) {
            logger.warn("Unable to create LDAPEntry from Contact: " + e.toString(), e);
            return null;
        }
    }

    // -------------------------------------------------------- Private methods

    /**
     * Check if the property has a value
     * TODO use StringUtils
     * @param pro
     *            Property to be checked
     * @return returns true is the value is empty, false if not.
     */
    private boolean propertyCheck(Property pro) {
        if (pro.getPropertyValueAsString() != null && !pro.getPropertyValueAsString().equals("")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Creates a unique identifier
     * 
     * @param contact
     *            contact needing an id
     * @return id of the contact
     */
    protected String createUniqueId(Contact contact) {
        Date ora = new Date();

        if (contact.getUid() == null)
            return "fnbl-id-" + Math.abs(((contact.toString() + ora.getTime()).hashCode()));
        else
            return contact.getUid() + "-" + ora.getTime();
    }

    // --------------------------------------------------------- Static methods

    /**
     * Compares two attribute sets
     * 
     * @param authoritativeSet
     *            reference set
     * @param compareSet
     *            comparative set
     * @return list of modifications to commit
     * @throws NamingException
     */
    public Map<String, Attributes> compareAttributeSets(Attributes authoritativeSet, Attributes compareSet)
            throws NamingException {

        Map<String, Attributes> modifications = new HashMap<String, Attributes>();
        Attributes delAttributes = new BasicAttributes();
        Attributes addAttributes = new BasicAttributes();
        Attributes replaceAttributes = new BasicAttributes();
        // List<LDAPModification> modifications = new
        // ArrayList<LDAPModification>();
        List<String> supportedAttrs = Arrays.asList(getSupportedAttributes());

        Iterator<String> it = supportedAttrs.iterator();

        // loop over supported attributes
        while (it.hasNext()) {
            String attribute = it.next();

            // skip unmodifiable attrs
            if (attribute.equals("modifyTimestamp"))
                continue;

            Attribute authoritaveAttribute = authoritativeSet.get(attribute);
            Attribute compareAttribute = compareSet.get(attribute);

            if (authoritaveAttribute == null || compareAttribute == null) {
                // remove an old attribute
                if (authoritaveAttribute == null && compareAttribute != null) {
                    delAttributes.put(compareAttribute);
                }

                // add a new attribute
                if (authoritaveAttribute != null && compareAttribute == null) {
                    addAttributes.put(authoritaveAttribute);
                }
            } else {
                // replace an attribute
                String authValue = (String) authoritaveAttribute.get();
                String compareValue = (String) compareAttribute.get();
                if (!authValue.equals(compareValue)) {
                    replaceAttributes.put(authoritaveAttribute);
                }
            }
        }
        modifications.put(DEL_ATTRIBUTE, delAttributes);
        modifications.put(REPLACE_ATTRIBUTE, replaceAttributes);
        modifications.put(ADD_ATTRIBUTE, addAttributes);

        return modifications;
    }

    public String getSoftDeleteAttribute() {
        return null;
    }

    public String getSoftDeleteFilter() {
        if (getSoftDeleteAttribute() != null) {
            return String.format("(!(%s=*))", getSoftDeleteAttribute());
        } else {
            return null;
        }
    }

    public String[] getSupportedAttributes() {
        return SUPPORTED_ATTRIBUTES;
    }

    public String getRdnAttribute() {
        return rdnAttribute;
    }

    public String getRdnValue(Attributes attrs) {
        String ret = null;
        if (attrs != null) {
            ret = LdapUtils.getPrintableAttribute(attrs.get(getRdnAttribute()));
        }
        return ret;
    }

    public String getTimestampAttribute() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getTwinItems(SyncItem si) {
        // TODO Auto-generated method stub
        return null;
    }

}