com.original.service.channel.core.ChannelService.java Source code

Java tutorial

Introduction

Here is the source code for com.original.service.channel.core.ChannelService.java

Source

/**
 * @className
 * 
 * Copyright (c) 2012, Original and/or its affiliates. All rights reserved.
 * ORIGINAL PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.original.service.channel.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.swing.event.EventListenerList;

import org.bson.types.ObjectId;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.google.code.morphia.query.Query;
import com.google.code.morphia.query.UpdateOperations;
import com.mongodb.Mongo;
import com.original.service.channel.AbstractService;
import com.original.service.channel.Account;
import com.original.service.channel.Attachment;
import com.original.service.channel.Channel;
import com.original.service.channel.ChannelAccount;
import com.original.service.channel.ChannelMessage;
import com.original.service.channel.Constants;
import com.original.service.channel.Protocol;
import com.original.service.channel.SeriveManager;
import com.original.service.channel.Service;
import com.original.service.channel.config.Initializer;
import com.original.service.channel.event.ChannelEvent;
import com.original.service.channel.event.ChannelListener;
import com.original.service.channel.event.MessageEvent;
import com.original.service.channel.event.MessageListner;
import com.original.service.channel.event.ServiceEvent;
import com.original.service.channel.event.ServiceListener;
import com.original.service.channel.protocols.email.services.EmailService;
import com.original.service.channel.protocols.im.iqq.QQService;
import com.original.service.channel.protocols.sns.weibo.WeiboService;
import com.original.service.people.People;
import com.original.service.people.PeopleManager;
import com.original.service.profile.Profile;

//Pending
//1. Data  View ? //2. ?  () ? //3. ???3 //4.
//??????(ChannelMessage) //5. ?
/**
 * 
 * ??
 * 1. ???Channel.json???
 * 2. ?????profile.json?
 * 3. ????????????
 * 4. ??????
 * 
 * ?
 * 1). ?????
 * 2) 
 * 2). ??????
 *       (1) ?????????
 *     (1.2)??Client??
 *       (2) ????
 *     (2.2)??Client??
 *     (3)?????
 *     (4)????
 *     LifeCycle(Created,    AUTHPENDING, STARTED,SUSPEND,STOPPING,CLOSED,FAILED) 
 * @author sxy 
 */
public final class ChannelService extends AbstractService implements SeriveManager, Constants {
    private Logger logger;
    //??
    private ChannelManager channelManager;
    //??
    private AccountManager accountManager;
    //??
    private MessageManager messageManager;
    //???
    private PeopleManager peopleManager;
    //??
    private HashMap<ChannelAccount, Service> serviceMap = new HashMap<ChannelAccount, Service>();
    private Morphia morphia;
    private Mongo mongo;
    private Datastore ds;
    private Initializer initializer;
    private static ExecutorService executor = Executors.newSingleThreadExecutor();
    private static ChannelService singleton;
    //??Pendign??(ChannelAccoutn)?
    //??
    @Deprecated
    private Vector<ChannelAccount> failedServiceAccounts = new Vector<ChannelAccount>();

    private Lock lock = new ReentrantLock(); //lock for serviceMap, for map is not thread safety

    /**
     * 
     * @return
     */
    public synchronized static ChannelService getInstance() {
        if (singleton == null) {
            singleton = new ChannelService();
        }
        return singleton;
    }

    /**
     * 
     */
    private ChannelService() {
        initMongoDB();
        //??
        init();
    }

    /**
     * ???
     */
    private void initMongoDB() { //?,  throws ChannelException
        //1 log
        logger = Logger.getLogger("channer");
        //2 db
        morphia = new Morphia();
        morphia.map(ChannelAccount.class);
        morphia.map(Channel.class);
        morphia.map(Profile.class);
        morphia.map(Attachment.class);
        morphia.map(People.class);
        logger.log(Level.INFO, "Mapping POJO to Mongo DB!");
        // DB
        try {
            mongo = new Mongo(Channel_DB_Server, Channel_DB_Server_Port);
            // db mapping to object
            ds = morphia.createDatastore(mongo, Channel_DB_Name);
            ds.ensureIndexes();
        } catch (Exception exp) {
            logger.log(Level.SEVERE, "To connect MongoDB Service fail!" + exp.toString());
            //Pending, if fail , should throw exception or exit.
            return;
        }
        //reread data from config
        initializer = new Initializer(mongo);
        try {
            initializer.init(false);
        } catch (Exception exp) {
            logger.log(Level.SEVERE, "To init channel db fail!" + exp.toString());
        }
        //4 serice managers
        //??
        messageManager = new MessageManager(this, morphia, mongo, ds);
        //??
        channelManager = new ChannelManager(this, mongo, morphia, ds);
        //????
        accountManager = new AccountManager(this, mongo, morphia, ds, channelManager);
        //??
        peopleManager = new PeopleManager(this, morphia, mongo, ds);
    }

    /**
     * ???
     */
    private void init() { //?channel?

        Runnable initServiceTask = new Runnable() {
            public void run() {
                try {
                    lock.lock();

                    HashMap<String, ChannelAccount> cas = accountManager.getChAccountMap();
                    for (String key : cas.keySet()) {
                        ChannelAccount ca = cas.get(key);
                        if (serviceMap.containsKey(ca))
                            continue;

                        try {
                            Service sc = createService(ca); //????????
                            if (sc != null) {
                                serviceMap.put(ca, sc);
                                sc.addMessageListener(new ChannelServiceListener());
                            }
                        } catch (Exception ex) {
                            System.err.println("Account startup error " + ca.getAccount().getName());
                            failedServiceAccounts.add(ca);
                        }
                    }
                } finally {
                    lock.unlock();
                }
                //???????
                start();
            }
        };

        executor.execute(initServiceTask);
    }

    /**
     * @return the peopleManager
     */
    public PeopleManager getPeopleManager() {
        return peopleManager;
    }

    /**
     * @param peopleManager the peopleManager to set
     */
    public void setPeopleManager(PeopleManager peopleManager) {
        this.peopleManager = peopleManager;
    }

    /**
     * @param serviceMap
     *            the serviceMap to set
     */
    public void setServiceMap(HashMap<ChannelAccount, Service> serviceMap) {
        this.serviceMap = serviceMap;
    }

    /**
     * @return the msgManager
     */
    public MessageManager getMsgManager() {
        return messageManager;
    }

    /**
     * @param msgManager
     *            the msgManager to set
     */
    public void setMsgManager(MessageManager msgManager) {
        this.messageManager = msgManager;
    }

    /**
     * @return the serviceMap
     */
    public HashMap<ChannelAccount, Service> getServiceMap() {
        return serviceMap;
    }

    /**
     * @return the morphia
     */
    public Morphia getMorphia() {
        return morphia;
    }

    /**
     * @param morphia
     *            the morphia to set
     */
    public void setMorphia(Morphia morphia) {
        this.morphia = morphia;
    }

    /**
     * @return the mongo
     */
    public Mongo getMongo() {
        return mongo;
    }

    /**
     * @param mongo
     *            the mongo to set
     */
    public void setMongo(Mongo mongo) {
        this.mongo = mongo;
    }

    /**
     * @return the ds
     */
    public Datastore getDs() {
        return ds;
    }

    /**
     * @param ds
     *            the ds to set
     */
    public void setDs(Datastore ds) {
        this.ds = ds;
    }

    /**
     * @return the logger
     */
    public java.util.logging.Logger getLogger() {
        return logger;
    }

    /**
     * @param logger
     *            the logger to set
     */
    public void setLogger(java.util.logging.Logger logger) {
        this.logger = logger;
    }

    /**
     * @return the notifyingListeners
     */
    public boolean isNotifyingListeners() {
        return notifyingListeners;
    }

    /**
     * @param notifyingListeners
     *            the notifyingListeners to set
     */
    public void setNotifyingListeners(boolean notifyingListeners) {
        this.notifyingListeners = notifyingListeners;
    }

    /**
     * @return the listenerList
     */
    public EventListenerList getListenerList() {
        return listenerList;
    }

    /**
     * @param listenerList
     *            the listenerList to set
     */
    public void setListenerList(EventListenerList listenerList) {
        this.listenerList = listenerList;
    }

    /**
     * ???????
     * Pending ?
     * @throws Exception
     */
    @Deprecated
    public synchronized void restartService() throws Exception {
        try {
            lock.lock();

            if (!failedServiceAccounts.isEmpty()) {
                ChannelAccount ca = failedServiceAccounts.firstElement();
                restartService(ca);
            }
        } finally {
            lock.unlock();
        }

    }

    /**
     *  Pending ?
     * @param ca
     * @throws Exception
     */
    @Deprecated
    public synchronized void restartService(ChannelAccount ca) throws Exception {
        try {
            lock.lock();

            Service sc = createService(ca);
            if (sc != null) {
                serviceMap.put(ca, sc);
                failedServiceAccounts.remove(ca);
                sc.addMessageListener(new ChannelServiceListener());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * ????
     * @param ca
     */
    @Deprecated
    public void skipService(ChannelAccount ca) {
        try {
            lock.lock();
            failedServiceAccounts.remove(ca);
        } finally {
            lock.unlock();
        }

    }

    /**
     * 
     */
    @Deprecated
    public void skipAllService() {
        try {
            lock.lock();
            failedServiceAccounts.clear();
        } finally {
            lock.unlock();
        }
    }

    /**
     * ????
     * @return
     */
    public boolean isStartupAll() {
        try {
            lock.lock();
            return failedServiceAccounts.isEmpty();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub

    }

    @Override
    public void suspend() {
        // TODO Auto-generated method stub

    }

    /**
     * ?????
     */
    @Override
    @Deprecated
    public synchronized void start() {
        //Pending:?
        //???push??
        //??????peopleID
        peopleManager.loadContracts();
        // weired way, but work anyway
        try {
            lock.lock();

            for (ChannelAccount key : serviceMap.keySet()) {
                // System.out.println("Key : " + key.toString() + " Value : "
                // + serviceMap.get(key));
                Service service = serviceMap.get(key);
                service.start();
            }
        } finally {
            lock.unlock();
        }

    }

    /**
     * Inner listener
     * 
     */
    protected class ChannelServiceListener implements MessageListner, ChannelListener, ServiceListener {

        @Override
        public void change(ServiceEvent evnt) {
            // TODO Auto-generated method stub
            System.out.println("ServiceEvent:" + evnt);
        }

        @Override
        public void change(ChannelEvent evnt) {
            // TODO Auto-generated method stub
            System.out.println("ChannelEvent:" + evnt);

            // proxy event to outer

        }

        @Override
        public void change(MessageEvent evnt) {
            // TODO Auto-generated method stub
            if (evnt.getType() == MessageEvent.Type_Added) {
                ChannelMessage[] chmsgs = evnt.getAdded();
                for (int i = 0; i < chmsgs.length; i++) {
                    // ??
                    People contract = peopleManager.getPeopleByMessage(chmsgs[0]);
                    //Every message must have peopleId(fromAddr)
                    //Pending (Service to do weibo service , bad code pattern, later push down to weibo service)
                    if (contract == null) {
                        contract = peopleManager.savePeople(chmsgs[0]);
                    }
                    if (contract != null) {
                        chmsgs[0].setPeopleId(contract.getId());
                        // ??
                        messageManager.save(chmsgs[0]);
                    }

                }
                fireMessageEvent(evnt); // notify to GUI App to add message only when save successfully!
            }
        }
    }

    // ///////////////////Event///////////////////////
    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type. The event instance is lazily created using the
     * parameters passed into the fire method.
     * 
     * @param e
     *            the event
     * @see EventListenerList
     */
    protected void fireMessageEvent(MessageEvent e) {
        notifyingListeners = true;
        try {
            // Guaranteed to return a non-null array
            Object[] listeners = listenerList.getListenerList();
            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == MessageListner.class) {
                    ((MessageListner) listeners[i + 1]).change(e);
                }
            }
        } finally {
            notifyingListeners = false;
        }
    }

    /**
     * Adds a listener for notification of any changes.
     * 
     * @param listener
     *            the <code>MessageListner</code> to add
     * @see Service#MessageListner
     */
    public void addMessageListener(MessageListner listener) {
        listenerList.add(MessageListner.class, listener);
    }

    /**
     * Removes a listener.
     * 
     * @param listener
     *            the <code>MessageListner</code> to add
     * @see Service#MessageListner
     */
    @Override
    public void removeMessageListener(MessageListner listener) {
        listenerList.remove(MessageListner.class, listener);
    }

    /**
     * 
     * @param listenerType
     * @return
     */
    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
        return listenerList.getListeners(listenerType);
    }

    /**
     * True will notifying listeners.
     */
    private transient boolean notifyingListeners;
    /**
     * The event listener list for the document.
     */
    protected EventListenerList listenerList = new EventListenerList();

    // ///////////////////////////////////

    @Override
    public int getStatus() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void put(String action, ChannelMessage msg) {
        // TODO Auto-generated method stub

        //Pending Song Xueyong: ????
        //1??????
        //2????msgIDID???
        //3??ID?
        //4???Message peopleid!!!
        if (msg != null) {
            //Must have PeopleID
            if (msg.getPeopleId() == null) {
                //            String toAddr = msg.getToAddr();
                People pp = peopleManager.getPeopleByMessage(msg);
                if (pp == null) {
                    pp = peopleManager.savePeople(msg);
                }
                if (pp == null) {
                    //Pending Exception definition for channel
                    throw new IllegalArgumentException("?" + msg.getToAddr());
                }

                msg.setPeopleId(pp.getId());
            }

            //1??
            if (action == Constants.ACTION_PUT_DRAFT) {
                if (msg.getId() != null) {//??????               
                    this.deleteMessage(msg.getId());
                }
                msg.setMessageID(getRandomMessageID());//??id
                msg.setDrafted(true);
                this.messageManager.save(msg);
                return;
            }

            //2?????
            ChannelAccount cha = msg.getChannelAccount();
            if (cha == null) { //??
                cha = getDefaultAccount(msg.getClazz());
            }
            if (cha != null) {
                //????idid?(?id?)
                preSendProcess(action, msg);
                try {
                    lock.lock();

                    Service sc = serviceMap.get(cha);
                    if (msg.getFromAddr() == null) {
                        msg.setFromAddr(cha.getAccount().getUser());
                    }
                    PutTask task = new PutTask(sc, action, msg);
                    Future monitor = ThreadManager.getInstance().submit(task);

                    //1??()???
                    if (msg.getToAddr().equals(cha.getAccount().getUser()))
                        return;
                    //2?????????
                    if (action == Constants.ACTION_QUICK_REPLY || action == Constants.ACTION_REPLY) {
                        if (msg.isWeibo()) {
                            return;
                        }
                    }

                    if (msg.getId() != null) { // ????               
                        this.deleteMessage(msg.getId());
                    }
                    msg.setType(ChannelMessage.TYPE_SEND);//?
                    msg.setProcessed(true);
                    messageManager.save(msg);
                } catch (Exception ex) {
                    ex.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    @Override
    public void post(String action, ChannelMessage msg) {
        if (msg != null) {
            try {
                lock.lock();
                preSendProcess(action, msg);

                ChannelAccount cha = msg.getChannelAccount();
                if (cha != null) {
                    Service sc = serviceMap.get(cha);
                    sc.post(action, msg);
                }
            } finally {
                lock.unlock();
            }
        }

    }

    /**
     * Pre deal 
     */
    private void preSendProcess(String action, ChannelMessage msg) {
        String msgId = msg.getMessageID();
        //forward, resend, transfer, quickreply
        if (msgId != null) {
            UUID idOne = UUID.randomUUID();
            msg.setMessageID(msgId + '$' + idOne);//add separator '$' for some use.
        }
    }

    private String getRandomMessageID() {
        long millis = System.currentTimeMillis();
        UUID idOne = UUID.randomUUID();
        return millis + "$" + idOne;
    }

    /**
     * ??ID
     * 
     * @param msg
     */
    public void deleteMessage(ObjectId id) {
        // get
        ChannelMessage msg = messageManager.getByID(id);
        // delete
        ds.delete(ChannelMessage.class, id);
        // event to outer
        ChannelMessage[] cmsg = new ChannelMessage[1];
        cmsg[0] = msg;
        MessageEvent evt = new MessageEvent(null, null, MessageEvent.Type_Deleted, null, cmsg, null);
        fireMessageEvent(evt);

    }

    /**
     *  ?ID?ID?).
     * @param msg
     */
    public void deleteMessage(String msgId) {
        // get
        Iterator<ChannelMessage> ite = messageManager.getByMessageID(msgId);

        ArrayList<ChannelMessage> bk = new ArrayList<ChannelMessage>();
        while (ite.hasNext()) {
            ChannelMessage chm = ite.next();
            bk.add(chm);
            ds.delete(ChannelMessage.class, chm.getId());
        }
        // delete
        //      ds.delete(ChannelMessage.class, ite);
        // event to outer
        ChannelMessage[] msgs = new ChannelMessage[bk.size()];
        bk.toArray(msgs);
        MessageEvent evt = new MessageEvent(null, null, MessageEvent.Type_Deleted, null, msgs, null);
        fireMessageEvent(evt);
    }

    /**
     *  Filter ?
     * @param msg
     */
    public void deleteMessages(Filter filter) {
        // get
        Iterator<ChannelMessage> ite = messageManager.getMessage(filter);

        ArrayList<ChannelMessage> bk = new ArrayList<ChannelMessage>();
        while (ite.hasNext()) {
            ChannelMessage chm = ite.next();
            bk.add(chm);
            ds.delete(ChannelMessage.class, chm.getId());
        }
        // delete by interator (exeption)
        //      ds.delete(ChannelMessage.class, ite);
        // event to outer
        ChannelMessage[] msgs = new ChannelMessage[bk.size()];
        bk.toArray(msgs);
        MessageEvent evt = new MessageEvent(null, null, MessageEvent.Type_Deleted, null, msgs, null);
        fireMessageEvent(evt);
    }

    public ChannelAccount getDefaultAccount(String msgClazz) {
        if (Constants.MAIL.equals(msgClazz)) {
            return getDefaultAccount(CHANNEL.MAIL);
        } else if (Constants.QQ.equals(msgClazz)) {
            return getDefaultAccount(CHANNEL.QQ);
        } else if (Constants.WEIBO.equals(msgClazz)) {
            return getDefaultAccount(CHANNEL.WEIBO);
        }
        return null;
    }

    public ChannelAccount getDefaultAccount(CHANNEL channel) {
        Query<ChannelAccount> q = ds.createQuery(ChannelAccount.class);
        switch (channel) {
        case MAIL:
            Pattern pattern = Pattern.compile("^email_.+$");// email_
            q.filter("account.channelName", pattern);
            break;

        case QQ:
            q.field("account.channelName").equal("im_qq");
            break;

        case WEIBO:
            q.field("account.channelName").equal("sns_weibo");
            break;
        }
        return q.get();
    }

    /**
     * 
     * @param userName
     * @return
     */
    public ChannelAccount getAccount(String userName) {
        Query<ChannelAccount> q = ds.createQuery(ChannelAccount.class).field("account.user")
                .endsWithIgnoreCase(userName);
        return q.get();
    }

    //   @Override
    //   public ChannelAccount getChannelAccount() {
    //      // TODO Auto-generated method stub
    //      return null;
    //   }

    //?????
    public List<Account> getAccounts() {
        return accountManager.getAccounts();
    }

    public void addChannelFromAccount(Account acc) {
        if (hasChannelByAccount(acc))
            return;

        Channel ch = new Channel();
        ch.setName(acc.getChannelName());
        ch.setVendor(acc.getVendor());
        ch.setType("email");
        ch.setStatus("running");
        ch.setDomain(acc.getDomain());
        Protocol[] protocols = new Protocol[2];
        protocols[0] = new Protocol(); //?
        protocols[0].setName(acc.getRecvProtocol());
        protocols[0].setType("incoming");
        protocols[0].setServer(acc.getRecvServer());
        protocols[0].setPort(acc.getRecvPort());

        protocols[1] = new Protocol();//???
        protocols[1].setName(acc.getSendProtocol());
        protocols[1].setType("outgoing");
        protocols[1].setServer(acc.getSendServer());
        protocols[1].setPort(acc.getSendPort());
        ch.setProtocols(protocols);

        ch.setContentType(new String[] { "html" });
        ch.setActions(new String[] { "send", "receive", "reply", "quick reply" });

        ds.save(ch);
    }

    public void delChannelByAccount(Account acc) {//
        if (acc != null && acc.getUser() != null) {
            Query<ChannelAccount> q = ds.createQuery(ChannelAccount.class).field("account.user")
                    .equal(acc.getUser());
            q.disableValidation();
            ds.delete(q);
        }
    }

    public void pushAccountToProfile(Account acc) {//profile
        if (acc != null) {
            Query<Profile> q = ds.createQuery(Profile.class); //??
            UpdateOperations<Profile> ops = ds.createUpdateOperations(Profile.class).add("accounts", acc);

            ds.update(q, ops, true);
        }
    }

    public void popAccountFromProfile(Account acc) {//profile?Accountequals()
        if (acc != null) {
            Query<Profile> q = ds.createQuery(Profile.class); //??
            UpdateOperations<Profile> ops = ds.createUpdateOperations(Profile.class).removeAll("accounts", acc);
            ds.update(q, ops, false);
        }
    }

    public boolean hasAccountInProfile(Account acc) {//?
        if (acc != null) {
            Query<Profile> q = ds.createQuery(Profile.class).retrievedFields(true, "accounts");
            Profile profile = q.get();
            if (profile != null) {
                Account[] accounts = profile.getAccounts();
                if (accounts != null && accounts.length > 0) {
                    for (Account account : accounts) {
                        if (account.equals(acc))
                            return true;
                    }
                }
            }
        }

        return false;
    }

    public boolean hasChannelByAccount(Account acc) {
        if (acc != null && acc.getName() != null) {
            Query<Channel> q = ds.createQuery(Channel.class).field("name").endsWithIgnoreCase(acc.getName());
            return q.iterator().hasNext();
        }
        return false;
    }

    // ///////////////////
    /**
     * ????
     * 
     * @param msg
     */
    public void trashMessage(ChannelMessage msg) {
        // TODO Auto-generated method stub
        if (msg == null) {
            return;
        }

        Integer trashFlag = msg.getFlags() != null ? msg.getFlags().get(ChannelMessage.FLAG_TRASHED) : null;
        Integer draftFlag = msg.getFlags() != null ? msg.getFlags().get(ChannelMessage.FLAG_DRAFT) : null;
        if (trashFlag != null && trashFlag.intValue() == 1) {// ??
            this.deleteMessage(msg.getId());
        } else if (draftFlag != null && draftFlag.intValue() == 1) {// ?
            this.deleteMessage(msg.getId());
        } else {
            this.updateMessageFlag(msg, ChannelMessage.FLAG_TRASHED, 1);
        }
    }

    /**
     * ??(???? 2?
     * 
     * @param msg
     * @param newValue
     */
    public void updateMessageStatus(ChannelMessage msg, String newValue) {

        // TODO Auto-generated method stub
        if (newValue == null || msg == null) {
            return;
        }
        ObjectId id = msg.getId();
        if (id == null) {
            return;
        }
        Query<ChannelMessage> chmsgQuery = ds.find(ChannelMessage.class).field("id").equal(id);

        UpdateOperations<ChannelMessage> ops = ds.createUpdateOperations(ChannelMessage.class).set("status",
                newValue);
        ds.update(chmsgQuery, ops, true);

        ChannelMessage newMsg = ds.find(ChannelMessage.class).field("id").equal(id).get();
        // fire event
        MessageEvent evt = new MessageEvent(this, null, MessageEvent.Type_Updated, null, null,
                new ChannelMessage[] { newMsg });
        this.fireMessageEvent(evt);
    }

    public void updateMessageFlag(ChannelMessage msg, String key, Object newValue) {
        updateMessageFlags(msg, new String[] { key }, new Object[] { newValue });
    }

    /**
     * ?
     * 
     * @param msg
     * @param key
     * @param newValue
     */
    public void updateMessageFlags(ChannelMessage msg, String[] keys, Object[] newValues) {

        // TODO Auto-generated method stub
        if (keys == null || newValues == null || msg == null) {
            return;
        }
        ObjectId id = msg.getId();
        if (id == null) {
            return;
        }

        int size = Math.min(keys.length, newValues.length);
        if (size < 1) {
            return;
        }

        Query<ChannelMessage> chmsgQuery = ds.find(ChannelMessage.class).field("id").equal(id);

        UpdateOperations<ChannelMessage> ops = ds.createUpdateOperations(ChannelMessage.class);
        ops.disableValidation();
        for (int i = 0; i < size; i++) {
            ops = ops.set("flags." + keys[i], newValues[i]);
        }

        ds.update(chmsgQuery, ops, true);
        ChannelMessage newMsg = ds.find(ChannelMessage.class).field("id").equal(id).get();

        // fire event
        MessageEvent evt = new MessageEvent(this, null, MessageEvent.Type_Updated, null, null,
                new ChannelMessage[] { newMsg });
        this.fireMessageEvent(evt);
    }

    /**
     * ?
     * 
     * @param msg
     * @param newAtts
     */
    public void updateMessage(ChannelMessage msg, HashMap<String, Object> newAtts) {
        // TODO Auto-generated method stub
        if (newAtts == null || newAtts.size() == 0 || msg == null) {
            return;
        }
        ObjectId id = msg.getId();
        if (id == null) {
            return;
        }
        Query<ChannelMessage> chmsgQuery = ds.find(ChannelMessage.class).field("id").equal(id);

        for (String key : newAtts.keySet()) {
            UpdateOperations<ChannelMessage> ops = ds.createUpdateOperations(ChannelMessage.class).set(key,
                    newAtts.get(key));
            ds.update(chmsgQuery, ops);
        }
        ChannelMessage newMsg = ds.find(ChannelMessage.class).field("id").equal(id).get();
        // fire event
        MessageEvent evt = new MessageEvent(this, null, MessageEvent.Type_Updated, null, null,
                new ChannelMessage[] { newMsg });
        this.fireMessageEvent(evt);

    }

    // /////////////////////////////

    private class PutTask implements Callable {

        String action;
        ChannelMessage msg;
        Service oneChannel;

        PutTask(Service oneChannel, String action, ChannelMessage msg) {
            this.oneChannel = oneChannel;
            this.action = action;
            this.msg = msg;
        }

        @Override
        public Object call() {
            // TODO Auto-generated method stub
            try {
                oneChannel.put(action, msg);
            } catch (Exception exp) {
                exp.printStackTrace();
            }
            return msg;
        }

    }

    /**
     * 
     * @author sxy
     *
     */
    private class MonitorRunable implements Runnable {
        Future monitor;

        MonitorRunable(Future monitor) {
            this.monitor = monitor;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (monitor.isDone()) {
                //fire Event to outer listner, the message deal over.
                try {
                    Thread.currentThread().join(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    }

    /**
     * 
     */
    @Override
    public List<Account> getContacts() {
        try {
            lock.lock();

            ArrayList<Account> all = new ArrayList<Account>();
            Collection<Service> scm = serviceMap.values();
            for (Service sc : scm) {
                try {
                    if (sc != null && sc.getContacts() != null) {
                        all.addAll(sc.getContacts());
                    }
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
            return all;
        } finally {
            lock.unlock();
        }
    }

    ////////////////////////////////////
    //   ??   ??????   Song   2013-2-11   2013-2-12      
    //   ??   ???   Song   2013-2-12   2013-2-13      

    @Override
    public void add(Service svc) {
    }

    @Override
    public void delelte(Service svc) {
    }

    @Override
    public void update(Service svc) {
    }

    @Override
    public Service get(String name) {
        return null;
    }

    @Override
    public java.util.Iterator<Service> Iterator() {
        return null;
    }

    @Override
    public void start(Service svc) {
    }

    @Override
    public void stop(Service svc) {
    }

    @Override
    public void suspend(Service svc) {
    }

    @Override
    public void resume(Service svc) {
    }

    @Override
    public void getStatus(Service svc) {
    }

    @Override
    public boolean isRunning(Service svc) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void resume() {
        // TODO Auto-generated method stub
    }

    @Override
    public boolean isRunning() {
        // TODO Auto-generated method stub
        return false;
    }

    /**
     * Pending Use Plug-in register to do this.
     * 
     * @param ca
     * @return
     */
    public synchronized static Service createService(ChannelAccount ca) throws Exception {
        if (ca.getChannel().getName().startsWith("email_")) {
            return new EmailService("Cydow", ca);

        } else if (ca.getChannel().getName().startsWith("im_qq")) {
            return new QQService("Cydow", ca);

        } else if (ca.getChannel().getName().startsWith("sns_weibo")) {
            return new WeiboService("Cydow", ca);
        }
        return null;
    }

}