fastpath源碼的一些對象

fastpath在前一章有作了一些介紹,是openfire提供的一種能夠實現客服功能的插件。 java

這裏把本身查看源碼看到一些主要幾個類來講明一下這個fastpath的主要對象。 sql

1.agent

這是對於的客服的實體對象,主要就是記錄一些基本的屬性,好比:agent的暱稱,jid等 編程

public class Agent {

	private static final Logger Log = LoggerFactory.getLogger(Agent.class);

    private static final String LOAD_AGENT =
            "SELECT name, agentJID, maxchats FROM fpAgent WHERE agentID=?";
    private static final String SAVE_AGENT =
            "UPDATE fpAgent SET name=?, agentJID=?, maxchats=? WHERE agentID=?";

    /**
     * The agent session created when the agent joined the service.
     */
    private AgentSession session;

    /**
     * The ceiling on the maximumn number of chats the agent should handle.
     */
    protected int maxChats = 0;

    /**
     * Nickname of the agent.
     */
    private String nickname;

    /**
     * Custom properties for the agent.
     */
    private JiveLiveProperties properties;

    /**
     * The id of the agent.
     */
    private long id;

    /**
     * The XMPP address of the Agent.
     */
    private JID agentJID;

2.agentManager

這個類很好理解就是對agent這個實體類的管理類,openfire中的一些實體對象同樣整個代碼主要是用一些sql語句實現了基本的增刪改查。 session

public class AgentManager {

	private static final Logger Log = LoggerFactory.getLogger(AgentManager.class);

    private static final String LOAD_AGENTS =
            "SELECT agentID FROM fpAgent";
    private static final String INSERT_AGENT =
            "INSERT INTO fpAgent (agentID, agentJID, name, maxchats, minchats) VALUES (?,?,?,?,?)";
    private static final String DELETE_AGENT =
            "DELETE FROM fpAgent WHERE agentID=?";
    private static final String DELETE_AGENT_PROPS =
            "DELETE FROM fpAgentProp WHERE ownerID=?";

3.agentSession

這個對象顧名思義agent的會話實體。從類的屬性能夠看出,這個實體是對應一個agent對象,包含了這個agent在各個技能組的聊天會話的集合,加入的技能組集合,以及這個agent的最大的聊天室,出席包的狀態等都在這個類中定義。這個是在後面操做中比較經常使用的類。以及一些事件的監聽類 mvc

public class AgentSession {

	private static final Logger Log = LoggerFactory.getLogger(AgentSession.class);

    private static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance("yyyyMMdd'T'HH:mm:ss", TimeZone.getTimeZone("GMT+0"));

    private Presence presence;
    private Collection<Workgroup> workgroups = new ConcurrentLinkedQueue<Workgroup>();
    private Offer offer;
    /**
     * Flag that indicates if the agent requested to get information of agents of all the workgroups
     * where the agent has joined.
     */
    private boolean requestedAgentInfo = false;
    private Map<Workgroup, Queue<ChatInfo>> chatInfos = new ConcurrentHashMap<Workgroup, Queue<ChatInfo>>();
    /**
     * By default maxChats has a value of -1 which means that #getMaxChats will return
     * the max number of chats per agent defined in the workgroup. The agent may overwrite
     * the default value but the new value will not be persisted for future sessions.
     */
    private int maxChats;
    private Agent agent;  private WorkgroupPresence workgroupPresenceHandler;
    private WorkgroupIQHandler workgroupIqHandler;
    private MessageHandler messageHandler;


4.workgroup

這個是技能組的實體對象,這個對象和以前的agent對象有點相同也是包含了基本的一些屬性,如:最大聊天數,這是對於技能組而言,offer超時時間等。這裏涉及到隊列的實體,一個技能組能夠包含多個隊列,根據隊列的等級來顯示路由的優先級。提供了建立隊列的方法,隊列會涉及到路由的實現。這個也是比較主要的類,在後面會常常使用。 ide

public class Workgroup {

	private static final Logger Log = LoggerFactory.getLogger(Workgroup.class);
    private String description = null;
    private Date creationDate;
    private Date modDate;
    private long offerTimeout = -1;
    private long requestTimeout = -1;
    private int maxChats;
    private int minChats;


    private Map<Long, RequestQueue> queues = new HashMap<Long, RequestQueue>();

    /**
     * Custom properties for the workgroup.
     */
    private JiveLiveProperties properties;

    private String workgroupName;
    private String displayName;
    private long id;

5.workgroupManager

這個也是對應的技能組的管理類,這個就比較複雜,涉及的地方比較多, 測試


6.workgroupPresence

這個顧名思義管理髮送給技能組的出席包,好比agent加入技能組就是個對應的技能組發送出席包。 this


7.RequestQueue

這個就是在前面提到的隊列,這個實體類,基本屬性主要是一些最大會話數,這裏就比較涉及多。 spa

用戶請求的鏈表集合,經過集合來添加,刪除等基本操做用戶的排隊請求,經過這個實現當前排隊人數的推送等 插件

private LinkedList<UserRequest> requests = new LinkedList<UserRequest>();
agentSession的集合對象,控制在本隊列中的agent,好比agent退出等
private AgentSessionList activeAgents = new AgentSessionList();
路由的實現類
/**
     * Dispatcher for the queue.
     */
    private RoundRobinDispatcher dispatcher;

8.Request

這個是個抽象類,就是請求的實體類。

三個實現類:

UserRequest:這個就是上面的隊列中的用戶請求的實體類,就是用戶加入排隊的時候發送的包,包括了一些路由的要求好比:agent:優先的agent,rank:排的技能組等級,timeout:隊列排隊的時間,微秒爲單位

TransferRequest:這個是後來實現轉接的時候看到,是agent端轉接給其餘agent進行請求的一個請求類,這裏的轉接是用涉及到轉接的對象,能夠轉接用戶,隊列,技能組均可以轉接。smack包也提供了對應的方法。

InvitationRequest:這個就是邀請的請求,好比fastpath在路由成功後,邀請用戶和agent加入本身建立的聊天室發送的包,就是這個類現實的。


9.RoundRobinDispatcher

這個也是上面一直提到的路由的實現類。整個的fastpath核心邏輯就是路由,就是這個類實現。該類是基於隊列的,從構造方法能夠看出經過定時器來掃描隊列中的用戶請求,來實現offer的分發給改隊列中的agent,以現實路由功能。

public RoundRobinDispatcher(RequestQueue queue) {
        this.queue = queue;
        agentList = new LinkedList<AgentSession>();
        properties = new JiveLiveProperties("fpDispatcherProp", queue.getID());
        try {
            info = infoProvider.getDispatcherInfo(queue.getWorkgroup(), queue.getID());
        }
        catch (NotFoundException e) {
            Log.error("Queue ID " + queue.getID(), e);
        }
        // Recreate the agentSelector to use for selecting the best agent to receive the offer
        loadAgentSelector();

        // Fill the list of AgentSessions that are active in the queue.  Once the list has been
        // filled this dispatcher will be notified when new AgentSessions join the queue or leave
        // the queue
        fillAgentsList();
        Log.error("after fillAgentsList");
        TaskEngine.getInstance().scheduleAtFixedRate(new TimerTask() {
            @Override
			public void run() {
                checkForNewRequests();
            }
        }, 2000, 2000);
    }

最核心的方法就是分發經過以前的請求時間,來作超時控制,查找最合適的agent分發offer,等待offer是否被接收,要是接收就分發邀請的請求給兩端加入聊天室從而實現了路由,若是沒接收或者超時就繼續循環,若是超時退出,就涉及到溢出,根據溢出的配置來對應的出來,好比取消請求告訴用戶等:

public void dispatch(Offer offer) {
        // The time when the request should timeout
    	//        long timeoutTime = System.currentTimeMillis() + info.getRequestTimeout();
    	//這裏修改,要是有輸入超時時間,不用默認的
        final Request request = offer.getRequest();
        long timeoutTime = System.currentTimeMillis() + ((request.getMetaData().containsKey("timeout"))?Long.parseLong(request.getMetaData().get("timeout").get(0)):info.getRequestTimeout());
        boolean canBeInQueue = request instanceof UserRequest;
        Map<String,List<String>> map = request.getMetaData();
        String initialAgent = map.get("agent") == null || map.get("agent").isEmpty() ? null : map.get("agent").get(0);
        String ignoreAgent = map.get("ignore") == null || map.get("ignore").isEmpty() ? null : map.get("ignore").get(0);
        // Log debug trace
        Log.debug("RR - Dispatching request: " + request + " in queue: " + queue.getAddress());

        // Send the offer to the best agent. While the offer has not been accepted send it to the
        // next best agent. If there aren't any agent available then skip this section and proceed
        // to overflow the current request
        if (!agentList.isEmpty()) {
            for (long timeRemaining = timeoutTime - System.currentTimeMillis();
                 !offer.isAccepted() && timeRemaining > 0 && !offer.isCancelled();
                 timeRemaining = timeoutTime - System.currentTimeMillis()) {

                try {
                    AgentSession session = getBestNextAgent(initialAgent, ignoreAgent, offer);
                    Log.error("AgentSession:");
                    if (session == null && agentList.isEmpty()) {
                        // Stop looking for an agent since there are no more agent available
                    	Log.error("agentList.isEmpty():");
                         break;
                    }
                    else if (session == null || offer.isRejector(session)) {
                    	Log.error("offer.isRejector(session):");
                        initialAgent = null;
                        Thread.sleep(1000);
                    }
                    else {
                        // Recheck for changed maxchat setting
                        Workgroup workgroup = request.getWorkgroup();
                        if (session.getCurrentChats(workgroup) < session.getMaxChats(workgroup)) {
                            // Set the timeout of the offer based on the remaining time of the
                            // initial request and the default offer timeout
                            timeRemaining = timeoutTime - System.currentTimeMillis();
                            //設置offer超時時長,是怕offer超時時長超過request時長
                            offer.setTimeout(timeRemaining < info.getOfferTimeout() ?
                                    timeRemaining : info.getOfferTimeout());

                            // Make the offer and wait for a resolution to the offer
                            if (!request.sendOffer(session, queue)) {
                                // Log debug trace
                                Log.debug("RR - Offer for request: " + offer.getRequest() +
                                        " FAILED TO BE SENT to agent: " +
                                        session.getJID());
                                continue;
                            }
                            // Log debug trace
                            Log.debug("RR - Offer for request: " + offer.getRequest() + " SENT to agent: " +
                                    session.getJID());
                            
                            offer.waitForResolution();
                            // If the offer was accepted, we send out the invites
                            // and reset the offer
                            if (offer.isAccepted()) {
                                // Get the first agent that accepted the offer
                                AgentSession selectedAgent = offer.getAcceptedSessions().get(0);
                                // Log debug trace
                                Log.debug("RR - Agent: " + selectedAgent.getJID() +
                                        " ACCEPTED request: " +
                                        request);
                                // Create the room and send the invitations
                                offer.invite(selectedAgent);
                                
                                //發送監控消息 bywengwh
                                sendUmccWorkMsg(request,selectedAgent);
                                
                                // Notify the agents that accepted the offer that the offer process
                                // has finished
                                for (AgentSession agent : offer.getAcceptedSessions()) {
                                    agent.removeOffer(offer);
                                }
                                if (canBeInQueue) {
                                    // Remove the user from the queue since his request has
                                    // been accepted
                                    queue.removeRequest((UserRequest) request);
                                }
                            }
                        }
                        else {
                            // Log debug trace
                            Log.debug("RR - Selected agent: " + session.getJID() +
                                    " has reached max number of chats");
                        }
                    }
                }
                catch (Exception e) {
                    Log.error(e.getMessage(), e);
                }
            }
        }
        if (!offer.isAccepted() && !offer.isCancelled()) {
            // Calculate the maximum time limit for an unattended request before cancelling it
        	//modify by Condy 2014.03.09   
        	//TODO 未測試
        	long requestTimeOut=(request.getMetaData().containsKey("timeout"))?Long.parseLong(request.getMetaData().get("timeout").get(0)):info.getRequestTimeout();
            long limit = request.getCreationTime().getTime() +
                    (requestTimeOut * (getOverflowTimes() + 1));

            if (limit - System.currentTimeMillis() <= 0 || !canBeInQueue) {
                // Log debug trace
                Log.debug("RR - Cancelling request that maxed out overflow limit or cannot be queued: " + request);
                // Cancel the request if it has overflowed 'n' times
                request.cancel(Request.CancelType.AGENT_NOT_FOUND);
            }
            else {
                // Overflow if request timed out and was not dispatched and max number of overflows
                // has not been reached yet
                overflow(offer);
                // If there is no other queue to overflow then cancel the request
                if (!offer.isAccepted() && !offer.isCancelled()) {
                    // Log debug trace
                    Log.debug("RR - Cancelling request that didn't overflow: " + request);
                    request.cancel(Request.CancelType.AGENT_NOT_FOUND);
                }
            }
        }
    }

這是幾個主要的類,對於整個fastpath插件類是不少的,功能不少,還等着你們多看源碼去挖掘。其實在看源碼的時候,能夠觀察他們的包的規劃,類的命名,fastpath主要是分紅:Provider,Handle,dispatcher,event等這幾個結尾類。從查看源碼明白他們的思路,可能不必定能作到每一個類都讀一遍,讀清楚,可是對於他們的編寫習慣有時候也能夠借鑑,對本身之後編寫也是個好處。雖然如今mvc是風靡java編程。這裏就把fastpath作個總結。後面看看會接觸到什麼新的東西或者本身學一些java的一些知識的時候在貼出來。

相關文章
相關標籤/搜索