1. 程式人生 > >openmeetings二次開發日誌(四) 4.0.2及openmeetings工具類

openmeetings二次開發日誌(四) 4.0.2及openmeetings工具類

過年事情太多,一個多月沒寫部落格了。今天來補一篇openmeetings的。話說在我上次寫完openmeetings4.0.0的安裝後,Apache又陸續更新了4.0.1和4.0.2版本,兩個版本都進行一些優化和改動。我兩個版本都簡單體驗了以下,總體感覺4.0.1版本不是很好,在進行過登入或註冊時出現過非常明顯的延遲現象。18年2月23日釋出了4.0.2版,這一般感覺優化還不錯,執行比之前流暢的多。貼一張官網的更新說明:

這裡寫圖片描述

優化和改進的功能中有很大一部分是聊天,房間中感覺最好的就是修復了重複使用者這一bug,使用者體驗性得到了極大的改善。

新版本linux版安裝和部署同4.0.0版本一樣,沒有區別,不知道怎麼安裝和部署的同學可以參考以下我的這篇部落格

linux下安裝Openmeetings4.0.0

好了,新版本的簡介暫時聊到這裡,接下來我會用一個現實遇到的需求做例子,講一講實際開發中怎麼利用openmeetings去實現我們的業務,展示一些我自己封裝的openmeetings的工具類給各位做實現業務的一些參考。先來看看目錄結構:

這裡寫圖片描述

圖中,om資料夾下存放與openmeetings的相關的所有類,fileWebService,roomWebService,userWebService這三個資料夾是存放openmeetings的webservice生成的檔案,剩下兩個類OpenmeetingsConstants是個介面,主要用於存放常量,OpenmeetingsServiceUtil是我封裝的工具類,來看看這個類的程式碼:

package com.avie.ltd.om;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import com.avie.ltd.om.fileWebService.FileExplorerObject;
import com.avie.ltd.om.fileWebService.FileItemDTO;
import com.avie.ltd.om.fileWebService.FileWebService;
import com.avie.ltd.om.fileWebService.OrgApacheOpenmeetingsWebserviceFileWebService;
import
com.avie.ltd.om.roomWebService.OrgApacheOpenmeetingsWebserviceRoomWebService; import com.avie.ltd.om.roomWebService.RoomDTO; import com.avie.ltd.om.roomWebService.RoomWebService; import com.avie.ltd.om.roomWebService.Type; import com.avie.ltd.om.userWebService.ExternalUserDTO; import com.avie.ltd.om.userWebService.OrgApacheOpenmeetingsWebserviceUserWebService; import com.avie.ltd.om.userWebService.RoomOptionsDTO; import com.avie.ltd.om.userWebService.ServiceResult; import com.avie.ltd.om.userWebService.UserWebService; /** * openmeetings的專用工具類 * * @author duyu * */ public class OpenmeetingsServiceUtil { /** * 執行緒安全的map物件。存放sid */ private static ConcurrentHashMap<String, String> sidMap = new ConcurrentHashMap<>(); private static OrgApacheOpenmeetingsWebserviceRoomWebService orgRoom = new OrgApacheOpenmeetingsWebserviceRoomWebService(); private static OrgApacheOpenmeetingsWebserviceUserWebService orgUser = new OrgApacheOpenmeetingsWebserviceUserWebService(); private static OrgApacheOpenmeetingsWebserviceFileWebService orgFile = new OrgApacheOpenmeetingsWebserviceFileWebService(); private static RoomWebService roomService; private static UserWebService userWebService; private static FileWebService fileWebService; /** * 得到RoomWebService 基於雙檢鎖的單例模式 * * @return */ private static RoomWebService getRoomWebService() { if (roomService == null) { synchronized (RoomWebService.class) { if (roomService == null) { try { roomService = orgRoom.getRoomWebServicePort(); } catch (Exception e) { // TODO: handle exception } } } } return roomService; } /** * 得到fileWebService * * @return */ private static FileWebService getFileWebService() { if (fileWebService == null) { synchronized (RoomWebService.class) { if (fileWebService == null) { try { fileWebService = orgFile.getFileWebServicePort(); } catch (Exception e) { // TODO: handle exception } } } } return fileWebService; } /** * 得到RoomWebService 基於雙檢鎖的單例模式 * * @return */ private static UserWebService getUserWebService() { if (userWebService == null) { synchronized (UserWebService.class) { if (userWebService == null) { try { userWebService = orgUser.getUserService(); } catch (Exception e) { // TODO: handle exception } } } } return userWebService; } /** * 得到某個登入使用者的sid */ public static String getSid() { if (!sidMap.isEmpty()) { String time = sidMap.get("time"); // 假如沒超過10分鐘 if (Long.valueOf(time) + 1000 * 60 * 10 <= System.currentTimeMillis()) { return sidMap.get("sid"); } } userWebService = getUserWebService(); if (userWebService == null) { return null; } try { ServiceResult rs = userWebService.login(OpenmeetingsConstants.USERNAME, OpenmeetingsConstants.PASSWORD); String sid = rs.getMessage(); sidMap.put("sid", sid); sidMap.put("time", String.valueOf(System.currentTimeMillis())); return sid; } catch (Exception e) { // TODO: handle exception return null; } } public static Long addRoom(String roomType, Long capacity, boolean allowRecording, boolean allowUserQuestions) { RoomDTO room = new RoomDTO(); room.setType(Type.fromValue(roomType)); room.setCapacity(capacity); room.setAllowRecording(allowRecording); room.setAllowUserQuestions(allowUserQuestions); RoomWebService roomService = getRoomWebService(); // 如果roomService沒拿到,說明openmeetings的服務沒起,進不去,返回-1表示異常 if (roomService == null) { return -1L; } String sid = getSid(); if(sid == null ) { return -1L; } room = roomService.add(sid, room); return room.getId(); } public static String getIntoRoom(Long roomId,boolean showAudioVideoTest,boolean moderator,boolean allowSameURLMultipleTimes,boolean allowRecording,ExternalUserDTO user) { RoomOptionsDTO options = new RoomOptionsDTO(); options.setRoomId(roomId); options.setShowAudioVideoTest(showAudioVideoTest); options.setModerator(moderator); options.setAllowSameURLMultipleTimes(allowSameURLMultipleTimes); options.setAllowRecording(allowRecording); UserWebService userWebService = getUserWebService(); if(userWebService == null) { return null; } String sid = getSid(); if(sid == null ) { return null; } ServiceResult serviceRs = userWebService.getRoomHash(sid, user, options); return serviceRs.getMessage(); } // public static void getRoomFile() { // FileWebService fileWebService = getFileWebService(); // FileExplorerObject ex = fileWebService.getRoom(getSid(), 6); // List<FileItemDTO> files = ex.getRoomHome(); // for(FileItemDTO dto : files) { //// dto.getha // } // } }

我簡單的講一下這段程式碼,說一下我的實現思路,從最上面的變數開始講:

這裡寫圖片描述

最開始的sidMap是一個ConcurrentHashMap的例項。ConcurrentHashMap是一個執行緒安全的map實現類,在這裡使用也是出於執行緒安全的考慮,因為sidMap中我主要存放了登入所需的sid(之前的部落格中講過sid主要是和我們平時登入的token類似的東西。),待會兒我會詳細講這個。往下走,orgRoom,orgUser,orgFile這三個東西主要是用於得到下面的roomService,userWebService,fileWebService這三個業務相關類的例項,用於實現openmeetings的相關業務。
往下走,是例項化三個業務類的方法;

/**
     * 得到RoomWebService 基於雙檢鎖的單例模式
     * 
     * @return
     */
    private static RoomWebService getRoomWebService() {
        if (roomService == null) {
            synchronized (RoomWebService.class) {
                if (roomService == null) {
                    try {
                        roomService = orgRoom.getRoomWebServicePort();
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            }
        }
        return roomService;
    }

    /**
     * 得到fileWebService
     * 
     * @return
     */
    private static FileWebService getFileWebService() {
        if (fileWebService == null) {
            synchronized (RoomWebService.class) {
                if (fileWebService == null) {
                    try {
                        fileWebService = orgFile.getFileWebServicePort();
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            }
        }
        return fileWebService;
    }

    /**
     * 得到UserWebService 基於雙檢鎖的單例模式
     * 
     * @return
     */
    private static UserWebService getUserWebService() {
        if (userWebService == null) {
            synchronized (UserWebService.class) {
                if (userWebService == null) {
                    try {
                        userWebService = orgUser.getUserService();
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            }
        }
        return userWebService;
    }

這裡使用雙檢鎖來保證這幾個業務實體類為單例,用雙檢鎖實現的單例模式好處在於延遲載入且有較好的效能。這裡不採用餓漢式的單例實現方法還有一種考慮,就是這幾個service的例項並不一定拿得到且有可能丟擲錯誤。因為你在執行roomService = orgRoom.getRoomWebServicePort();這一句程式碼時,是會發起一次對openmeetings的請求的,假如這時候openmeetings的服務沒有起起來,會導致這裡出現異常。如果用餓漢式的單例實現方法,直接RoomWebService roomService = orgRoom.getRoomWebServicePort();就有可能導致該類初始化失敗,進而導致整個伺服器啟動失敗。而對於我們的業務而言,openmeetings的服務起不起不應該影響到我們這邊的業務服務,所以我採用這種方式來初始化幾個service類的例項。

這裡寫圖片描述

接下來是一個靜態方法,主要用於得到sid並存入上面提到過的ConcurrentHashMap中。這裡,sid其實是你呼叫userWebService的登入方法後這句程式碼後得到的值,這個值是你呼叫其他WebService介面的必傳的一個值,可以看做登入的token。這裡要拿到這個值你必須要傳入一個具有soap許可權的內部使用者名稱和密碼。怎麼知道該使用者有沒有soap許可權楠?用最開始你建立openmeetings的時設定的超級管理員登上去,選擇使用者管理即可檢視是否有soap許可權,如下圖:

這裡寫圖片描述

這裡用一個map來存sid,而不用一個String型別的變數來指向sid,是因為sid是有時間限制的,openmeetings裡面好像是預設(之前在哪裡看到過,記不清楚了)一個sid只有15分鐘的生存時間,過了15分鐘之後就會自動失效,這時你需要重新登入重新拿一次sid。所以我這裡就考慮把sid存在map中,並記錄下它的存入時間,當下次需要用到時,拿到他上次的存入時間進行比對,如果沒超過10分鐘,則返回當前值,如果超過,則重新請求一次登入方法,拿到一個新的sid存入並返回。

程式碼繼續往下走:

這裡寫圖片描述

一個封裝的建立房間的方法,需要傳入roomType(房間型別),capacity(房間最大人數),allowRecording(是否允許記錄),建立一個RoomDTO的物件,設定它的引數,然後得到roomService的例項,判斷一下非空,拿到sid,呼叫roomService的add方法進行新增,然後拿到新增後room物件的id值進行返回,這個方法用於建立一個自定義的房間。

再往下走:

這裡寫圖片描述

這個方法是進入房間的方法,跟上面那個方法大同小異,該方法傳入進入房間需要的一些引數,諸如房間id,是否為主持人等引數,然後得到userWebService,呼叫getRoomHash這個方法得到進入的房間的金鑰返回。

到這裡,一個基本的openmeetings的工具類就實現了,我們來看它實際的呼叫情況。
先說一說我的具體需求:老師和學生在我們現有的系統中註冊賬號,然後在我們的系統中點選進入課堂,跳轉到openmeetings的房間中進行視訊通話並用白板進行授課。
下面是業務程式碼的一部分:

這裡寫圖片描述

getStudentUrl這個方法主要用於拿到跳轉openmeetings房間的url,從外面傳入三個引數,user為學生的實體物件,classSystem可以看做房間的最大限制人數,teacher是老師的實體物件。進入方法先判斷該老師有roomId,也就是之前是否為該老師建立過房間,如果沒有,呼叫OpenmeetingsServiceUtil我的openmeetings工具類建立一個房間,將得到的roomId存資料庫中,然後呼叫 OpenmeetingsServiceUtil的getIntoRoom方法得到進入該房間的金鑰,拼接成url返回,前臺拿到這個url跳轉就可以進入房間了。
業務層的呼叫我就暫時講到這,大家做個參考就行了。畢竟每個公司的業務都不一樣,講多了也沒意義。

這些程式碼總體基於我之前部落格中利用webService呼叫openmeetings的實現,照自己的理解進行了一些封裝和解耦。目前我給公司開發的這套系統還屬於一個測試版,後面還會進行不斷的完善並增強功能(比如修改openmeetings的頁面,拿到openmeetings的檔案之內的),所以後面應該會在其他部落格中摻雜一些openmeetings最新進度的部落格。喜歡的可以跟進一下。