/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * 12.10.2011 by frentix GmbH, http://www.frentix.com * <p> */ package org.olat.modules.openmeetings.manager; import java.io.File; import java.net.ConnectException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; import javax.annotation.PostConstruct; import javax.ws.rs.core.UriBuilder; import javax.xml.ws.BindingProvider; import org.apache.openmeetings.axis.services.GetRoomsWithCurrentUsersByListAndType; import org.apache.openmeetings.axis.services.RoomService; import org.apache.openmeetings.axis.services.RoomServicePortType; import org.apache.openmeetings.axis.services.UserService; import org.apache.openmeetings.axis.services.UserServicePortType; import org.apache.openmeetings.axis.services.xsd.RoomReturn; import org.apache.openmeetings.axis.services.xsd.RoomUser; import org.apache.openmeetings.persistence.beans.basic.xsd.Sessiondata; import org.apache.openmeetings.persistence.beans.flvrecord.xsd.FlvRecording; import org.apache.openmeetings.persistence.beans.room.xsd.Room; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.id.UserConstants; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.WebappHelper; import org.olat.core.util.cache.CacheWrapper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.group.BusinessGroup; import org.olat.group.DeletableGroupData; import org.olat.modules.openmeetings.OpenMeetingsModule; import org.olat.modules.openmeetings.model.OpenMeetingsRecording; import org.olat.modules.openmeetings.model.OpenMeetingsRoom; import org.olat.modules.openmeetings.model.OpenMeetingsRoomReference; import org.olat.modules.openmeetings.model.OpenMeetingsUser; import org.olat.modules.openmeetings.model.RoomReturnInfo; import org.olat.repository.RepositoryManager; import org.olat.repository.model.RepositoryEntryShortImpl; import org.olat.user.DisplayPortraitManager; import org.olat.user.UserDataDeletable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * @author srosse, stephae.rosse@frentix.com */ @Service public class OpenMeetingsManagerImpl implements OpenMeetingsManager, UserDataDeletable, DeletableGroupData { private final static OLog log = Tracing.createLoggerFor(OpenMeetingsManagerImpl.class); @Autowired private OpenMeetingsDAO openMeetingsDao; @Autowired private OpenMeetingsModule openMeetingsModule; @Autowired private CoordinatorManager coordinator; @Autowired private RepositoryManager repositoryManager; private CacheWrapper<String,Long> sessionCache; private OpenMeetingsLanguages languagesMapping; @PostConstruct public void init() { languagesMapping = new OpenMeetingsLanguages(); languagesMapping.read(); sessionCache = coordinator.getCoordinator().getCacher().getCache(OpenMeetingsManager.class.getSimpleName(), "session"); } @Override public Long getRoomId(BusinessGroup group, OLATResourceable ores, String subIdentifier) { OpenMeetingsRoomReference prop = openMeetingsDao.getReference(group, ores, subIdentifier); if(prop == null) { return null; } return prop.getRoomId(); } @Override public List<OpenMeetingsRoom> getOpenOLATRooms() { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); GetRoomsWithCurrentUsersByListAndType getRooms = new GetRoomsWithCurrentUsersByListAndType(); getRooms.setAsc(true); getRooms.setExternalRoomType(getOpenOLATExternalType()); getRooms.setOrderby("name"); getRooms.setStart(0); getRooms.setMax(2000); getRooms.setSID(adminSID); Map<Long,RoomReturnInfo> realRooms = new HashMap<Long,RoomReturnInfo>(); //get rooms on openmeetings List<RoomReturn> roomsRet = roomWs.getRoomsWithCurrentUsersByListAndType(adminSID, 0, 2000, "name", true, getOpenOLATExternalType()); if(roomsRet != null) { for(RoomReturn roomRet:roomsRet) { RoomReturnInfo info = new RoomReturnInfo(); info.setName(roomRet.getName()); info.setRoomId(roomRet.getRoomId()); int numOfUsers = 0; if(roomRet.getRoomUser() != null) { for(RoomUser user:roomRet.getRoomUser()) { if(user != null) { numOfUsers++; } } } info.setNumOfUsers(numOfUsers); realRooms.put(new Long(roomRet.getRoomId()), info); } } //get properties saved List<OpenMeetingsRoomReference> props = openMeetingsDao.getReferences(); Map<Long,String> roomIdToResources = getResourceNames(props); List<OpenMeetingsRoom> rooms = new ArrayList<OpenMeetingsRoom>(); for(OpenMeetingsRoomReference prop:props) { Long roomId = new Long(prop.getRoomId()); RoomReturnInfo infos = realRooms.get(roomId); if(infos != null) { OpenMeetingsRoom room = openMeetingsDao.deserializeRoom(prop.getConfig()); room.setReference(prop); room.setName(infos.getName()); room.setNumOfUsers(infos.getNumOfUsers()); String resourceName = roomIdToResources.get(roomId); if(resourceName != null) { room.setResourceName(resourceName); } rooms.add(room); } } return rooms; } catch (Exception e) { log.error("", e); return null; } } private Map<Long,String> getResourceNames(List<OpenMeetingsRoomReference> properties) { Map<Long,String> roomIdToResourceName = new HashMap<Long,String>(); Map<Long,List<Long>> resourceIdToRoomIds = new HashMap<Long,List<Long>>(); for(OpenMeetingsRoomReference prop:properties) { long roomId = prop.getRoomId(); if(prop.getGroup() != null) { roomIdToResourceName.put(roomId, prop.getGroup().getName()); } else if("CourseModule".equals(prop.getResourceTypeName())) { if(!resourceIdToRoomIds.containsKey(prop.getResourceTypeId())) { resourceIdToRoomIds.put(prop.getResourceTypeId(), new ArrayList<Long>()); } resourceIdToRoomIds.get(prop.getResourceTypeId()).add(roomId); } } if(!resourceIdToRoomIds.isEmpty()) { List<RepositoryEntryShortImpl> shortRepos = repositoryManager.loadRepositoryEntryShortsByResource(resourceIdToRoomIds.keySet(), "CourseModule"); for(RepositoryEntryShortImpl repoEntry : shortRepos) { List<Long> roomIds = resourceIdToRoomIds.get(repoEntry.getOlatResource().getResourceableId()); for(Long roomId:roomIds) { roomIdToResourceName.put(roomId, repoEntry.getDisplayname()); } } } return roomIdToResourceName; } @Override public String getURL(Identity identity, long roomId, String securedHash, Locale locale) { StringBuilder sb = new StringBuilder(); sb.append(openMeetingsModule.getOpenMeetingsURI().toString()); if(sb.lastIndexOf("/") != (sb.length() - 1)) { sb.append("/"); } sb.append("?secureHash=").append(securedHash) .append("&scopeRoomId=").append(roomId) .append("&language=").append(languagesMapping.getLanguageId(locale)) .append("&user_id=").append(getOpenOLATUserExternalId(identity)) .append("&wwwroot=").append(Settings.getServerContextPathURI()); return sb.toString(); } @Override public String setUserToRoom(Identity identity, long roomId, boolean moderator) throws OpenMeetingsException { try { UserServicePortType userWs = getUserWebService(); String sid = adminLogin(); int becomeModeratorAsInt = (moderator ? 1 : 0); String email = identity.getUser().getProperty(UserConstants.EMAIL, null); String externalUserId = getOpenOLATUserExternalId(identity); String externalUserType = getOpenOLATExternalType(); String firstname = identity.getUser().getProperty(UserConstants.FIRSTNAME, null); String lastname = identity.getUser().getProperty(UserConstants.LASTNAME, null); String profilePictureUrl = getPortraitURL(identity); String username = identity.getName(); String hashedUrl = userWs.setUserObjectAndGenerateRoomHashByURLAndRecFlag(sid, username, firstname, lastname, profilePictureUrl, email, externalUserId, externalUserType, roomId, becomeModeratorAsInt, 0, 1); if(hashedUrl.startsWith("-") && hashedUrl.length() < 5) { throw new OpenMeetingsException(parseErrorCode(hashedUrl)); } return hashedUrl; } catch(OpenMeetingsException e) { log.error("", e); throw e; } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } private long parseErrorCode(String errorCode) { try { return Integer.parseInt(errorCode); } catch (NumberFormatException e) { return OpenMeetingsException.UNKOWN; } } private String getPortraitURL(Identity identity) { File portrait = DisplayPortraitManager.getInstance().getBigPortrait(identity.getName()); if(portrait == null || !portrait.exists()) { return ""; } String key = UUID.randomUUID().toString().replace("-", ""); sessionCache.put(key, identity.getKey()); StringBuilder sb = new StringBuilder(); sb.append(Settings.getServerContextPathURI()) .append("/restapi/openmeetings/") .append(key) .append("/portrait"); return sb.toString(); } @Override public Long getIdentityKey(String token) { return sessionCache.get(token); } @Override public String setGuestUserToRoom(String firstname, String lastname, long roomId) throws OpenMeetingsException { try { String adminSessionId = adminLogin(); String username = UUID.randomUUID().toString().replace("-", ""); String email = ""; String externalUserId = getOpenOLATUserExternalId(username); String externalUserType = getOpenOLATExternalType(); String profilePictureUrl = ""; String hashedUrl = getUserWebService() .setUserObjectAndGenerateRoomHashByURL(adminSessionId, username, firstname, lastname, profilePictureUrl, email, externalUserId, externalUserType, roomId, 0, 0); if(hashedUrl.startsWith("-") && hashedUrl.length() < 5) { throw new OpenMeetingsException(parseErrorCode(hashedUrl)); } return hashedUrl; } catch(OpenMeetingsException e) { log.error("", e); throw e; } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } @Override public OpenMeetingsRoom getRoom(BusinessGroup group, OLATResourceable ores, String subIdentifier) throws OpenMeetingsException { OpenMeetingsRoomReference prop = openMeetingsDao.getReference(group, ores, subIdentifier); if(prop == null) { return null; } Long roomId = prop.getRoomId(); if(roomId != null && roomId.longValue() > 0) { try { String sessionId = adminLogin(); OpenMeetingsRoom room = openMeetingsDao.deserializeRoom(prop.getConfig()); getRoomById(sessionId, room, roomId.longValue()); return room; } catch(OpenMeetingsException e) { throw e; } catch(Exception e) { log.error("", e); throw translateException(e, 0); } } return null; } @Override public OpenMeetingsRoom getLocalRoom(BusinessGroup group, OLATResourceable ores, String subIdentifier) { OpenMeetingsRoomReference ref = openMeetingsDao.getReference(group, ores, subIdentifier); if(ref == null) { return null; } OpenMeetingsRoom room = openMeetingsDao.deserializeRoom(ref.getConfig()); room.setReference(ref); return room; } private OpenMeetingsRoom getRoomById(String sid, OpenMeetingsRoom room, long roomId) throws OpenMeetingsException { try { RoomServicePortType roomWs = getRoomWebService(); Room omRoom = roomWs.getRoomById(sid, roomId); if(omRoom != null) { room.setComment(omRoom.getComment()); if(omRoom.isIsModeratedRoom() != null) { room.setModerated(omRoom.isIsModeratedRoom()); } if(omRoom.isIsAudioOnly() != null) { room.setAudioOnly(omRoom.isIsAudioOnly()); } room.setName(omRoom.getName()); if(omRoom.getRoomsId() != null) { room.setRoomId(omRoom.getRoomsId()); } else { room.setRoomId(roomId); } room.setSize(omRoom.getNumberOfPartizipants()); room.setType(omRoom.getRoomtype().getRoomtypesId()); room.setClosed(omRoom.isIsClosed()); return room; } else { return null; } } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } private OpenMeetingsException translateException(Exception e, long ret) { long type = OpenMeetingsException.UNKOWN; if(ret < 0) { type = ret; } else { Throwable cause = e.getCause(); if(cause instanceof ConnectException && cause.getMessage() != null && cause.getMessage().contains("onnection refused")) { type = OpenMeetingsException.SERVER_NOT_AVAILABLE; } } return new OpenMeetingsException(e, type); } public OpenMeetingsRoom openRoom(OpenMeetingsRoom room) throws OpenMeetingsException { return closeOpenMeetingsRoom(room, false); } public OpenMeetingsRoom closeRoom(OpenMeetingsRoom room) throws OpenMeetingsException { return closeOpenMeetingsRoom(room, true); } /** * In OpenMeetings, close can mean open :-) * @param roomId The room id * @param status false = close, true = open * @throws OpenMeetingsException */ private OpenMeetingsRoom closeOpenMeetingsRoom(OpenMeetingsRoom room, boolean status) throws OpenMeetingsException { int responseCode = 0; try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); //OpenMeetings doc: false = close, true = open log.audit("Room state changed (true = close, false = open): " + status); responseCode = roomWs.closeRoom(adminSID, room.getRoomId(), status); if(responseCode < 0) { throw new OpenMeetingsException(responseCode); } return getRoomById(adminSID, room, room.getRoomId()); } catch(OpenMeetingsException e) { log.error("", e); throw e; } catch (Exception e) { log.error("", e); throw translateException(e, responseCode); } } @Override public List<OpenMeetingsRecording> getRecordings(long roomId) throws OpenMeetingsException { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); List<FlvRecording> recordings = roomWs.getFlvRecordingByRoomId(adminSID, roomId); List<OpenMeetingsRecording> recList = new ArrayList<OpenMeetingsRecording>(); if(recordings != null) { for(FlvRecording recording:recordings) { if(recording != null) { OpenMeetingsRecording rec = new OpenMeetingsRecording(); rec.setRoomId(recording.getRoomId()); rec.setRecordingId(recording.getFlvRecordingId()); rec.setFilename(recording.getFileName()); rec.setDownloadName(recording.getFileHash()); rec.setDownloadNameAlt(recording.getAlternateDownload()); rec.setPreviewImage(recording.getPreviewImage()); rec.setWidth(recording.getFlvWidth()); rec.setHeight(recording.getFlvHeight()); recList.add(rec); } } } return recList; } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } @Override public String getRecordingURL(OpenMeetingsRecording recording) throws OpenMeetingsException { try { String sid = adminLogin(); String url = UriBuilder.fromUri(openMeetingsModule.getOpenMeetingsURI()).path("DownloadHandler") .queryParam("fileName", recording.getDownloadName()) .queryParam("moduleName", "lzRecorderApp") .queryParam("parentPath", "") .queryParam("room_id", Long.toString(recording.getRoomId())) .queryParam("sid", sid).build().toString(); return url; } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } @Override public OpenMeetingsRoom addRoom(BusinessGroup group, OLATResourceable ores, String subIdentifier, OpenMeetingsRoom room) { if(room.getRoomId() < 0) { updateRoom(group, ores, subIdentifier, room); } try { String sid = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); long returned = roomWs.addRoomWithModerationAndRecordingFlags(sid, room.getName(), room.getType(), room.getComment(), room.getSize(), false, false, false, 0, room.isModerated(), getOpenOLATExternalType(), true, room.isAudioOnly(), false, true); if(returned >= 0) { room.setRoomId(returned); log.audit("Room created"); OpenMeetingsRoomReference ref = openMeetingsDao.createReference(group, ores, subIdentifier, room); room.setReference(ref); return room; } return null; } catch (Exception e) { log.error("", e); return null; } } private String getOpenOLATUserExternalId(Identity identity) { return getOpenOLATUserExternalId(identity.getName()); } private String getOpenOLATUserExternalId(String username) { return username + "@" + WebappHelper.getInstanceId(); } @Override public String getOpenOLATExternalType() { return "openolat_" + WebappHelper.getInstanceId(); } @Override public OpenMeetingsRoom updateRoom(BusinessGroup group, OLATResourceable ores, String subIdentifier, OpenMeetingsRoom room) { try { String sid = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); long returned = roomWs.updateRoomWithModerationQuestionsAudioTypeAndHideOptions(sid, room.getRoomId(), room.getName(), room.getType(), room.getComment(), room.getSize(), false, false, false, 0, room.isModerated(), false, room.isAudioOnly(), false, false, false, false, false, false, false); if(returned >= 0) { log.audit("Room updated"); openMeetingsDao.updateReference(group, ores, subIdentifier, room); return room; } return null; } catch (Exception e) { log.error("", e); return null; } } @Override public boolean deleteRoom(OpenMeetingsRoom room) { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); long ret = roomWs.deleteRoom(adminSID, room.getRoomId()); boolean ok = ret > 0; if(ok && room.getReference() != null) { openMeetingsDao.delete(room.getReference()); } return ok; } catch (Exception e) { log.error("", e); return false; } } @Override public boolean deleteRecording (OpenMeetingsRecording recording) { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); boolean resp = roomWs.deleteFlvRecording(adminSID, recording.getRecordingId()); return resp; } catch (Exception e) { log.error("", e); return false; } } @Override public List<OpenMeetingsUser> getUsersOf(OpenMeetingsRoom room) throws OpenMeetingsException { try { String adminSID = adminLogin(); RoomServicePortType roomWs = getRoomWebService(); RoomReturn roomClRet = roomWs.getRoomWithClientObjectsById(adminSID, room.getRoomId()); if(roomClRet != null) { List<RoomUser> userArr = roomClRet.getRoomUser(); return convert(userArr); } return Collections.emptyList(); } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } private List<OpenMeetingsUser> convert(List<RoomUser> clients) { List<OpenMeetingsUser> users = new ArrayList<OpenMeetingsUser>(); if(clients != null) { for(RoomUser client:clients) { OpenMeetingsUser user = convert(client); if(user != null) { users.add(user); } } } return users; } private OpenMeetingsUser convert(RoomUser client) { if(client == null) { return null; } OpenMeetingsUser user = new OpenMeetingsUser(); user.setPublicSID(client.getPublicSID()); user.setFirstName(client.getFirstname()); user.setLastName(client.getLastname()); return user; } @Override public boolean removeUser(String publicSID) { try { String adminSID = adminLogin(); boolean kickResponse = getUserWebService().kickUserByPublicSID(adminSID, publicSID); return kickResponse; } catch (Exception e) { log.error("", e); return false; } } @Override public boolean removeUsersFromRoom(OpenMeetingsRoom room) { try { String adminSID = adminLogin(); boolean kickResponse = getRoomWebService().kickUser(adminSID, room.getRoomId()); return kickResponse; } catch (Exception e) { log.error("", e); return false; } } private String getSessionID() { try { Sessiondata getSessionResponse = getUserWebService().getSession(); String sessionId = getSessionResponse.getSessionId(); return sessionId; } catch (Exception e) { log.error("", e); return null; } } private String adminLogin() throws OpenMeetingsException { long returnCode = 0; try { String sid = getSessionID(); String username = openMeetingsModule.getAdminLogin(); String userpass = openMeetingsModule.getAdminPassword(); returnCode = getUserWebService().loginUser(sid, username, userpass); if(returnCode > 0) { return sid; } throw new OpenMeetingsException(returnCode); } catch(OpenMeetingsException e) { log.error("", e); throw e; } catch (Exception e) { log.error("", e); throw translateException(e, returnCode); } } @Override public void deleteAll(BusinessGroup group, OLATResourceable ores, String subIdentifier) throws OpenMeetingsException { try { Long roomId = getRoomId(group, ores, subIdentifier); if(roomId != null) { OpenMeetingsRoom room = getRoom(group, ores, subIdentifier); if(room != null) { deleteRoom(room); } } } catch (OpenMeetingsException e) { log.error("", e); } } @Override public boolean checkConnection(String url, String login, String password) throws OpenMeetingsException { long returnCode; try { UserService ss = new UserService(); UserServicePortType port = ss.getUserServiceHttpSoap11Endpoint(); String endPoint = cleanUrl(url) + "/services/UserService?wsdl"; ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPoint); Sessiondata sessiondata = port.getSession(); String sid = sessiondata.getSessionId(); returnCode = getUserWebService().loginUser(sid, login, password); if(returnCode > 0) { return StringHelper.containsNonWhitespace(sid); } throw new OpenMeetingsException(returnCode); } catch(OpenMeetingsException e) { log.error("", e); throw e; } catch (Exception e) { log.error("", e); throw translateException(e, 0); } } @Override public void deleteUserData(Identity identity, String newDeletedUserName, File archivePath) { // } @Override public boolean deleteGroupDataFor(BusinessGroup group) { boolean allOk = true; OpenMeetingsRoom room = getLocalRoom(group, null, null); if(room != null) { allOk &= deleteRoom(room); } return allOk; } private final RoomServicePortType getRoomWebService() { RoomService ss = new RoomService(); RoomServicePortType port = ss.getRoomServiceHttpSoap11Endpoint(); String endPoint = getOpenMeetingsEndPoint() + "RoomService?wsdl"; ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPoint); return port; } private final UserServicePortType getUserWebService() { UserService ss = new UserService(); UserServicePortType port = ss.getUserServiceHttpSoap11Endpoint(); String endPoint = getOpenMeetingsEndPoint() + "UserService?wsdl"; ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPoint); return port; } private String getOpenMeetingsEndPoint() { return cleanUrl(openMeetingsModule.getOpenMeetingsURI().toString()) + "/services/"; } private String cleanUrl(String url) { return url.endsWith("/") ? url.substring(0, url.length() - 1) : url; } }