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版本一樣,沒有區別,不知道怎麼安裝和部署的同學可以參考以下我的這篇部落格
好了,新版本的簡介暫時聊到這裡,接下來我會用一個現實遇到的需求做例子,講一講實際開發中怎麼利用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最新進度的部落格。喜歡的可以跟進一下。