This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 401e498  [OPENMEETINGS-2239, OPENMEETINGS-677] sip transport with user 
count is added to the room
401e498 is described below

commit 401e4981c184764d21036204a75ef5dd3098c36e
Author: Maxim Solodovnik <solomax...@gmail.com>
AuthorDate: Thu Oct 22 12:35:47 2020 +0700

    [OPENMEETINGS-2239, OPENMEETINGS-677] sip transport with user count is 
added to the room
---
 openmeetings-core/pom.xml                          |   8 +
 .../openmeetings/core/remote/KurentoHandler.java   |  85 ++---
 .../openmeetings/core/remote/StreamProcessor.java  |  15 +-
 .../apache/openmeetings/core/sip/SipManager.java   | 108 +++++--
 .../core/util/ChatWebSocketHelper.java             |   5 +-
 .../openmeetings/core/util/WebSocketHelper.java    |  33 +-
 openmeetings-db/pom.xml                            |   9 -
 .../db/dao/calendar/AppointmentDao.java            |   2 +-
 .../apache/openmeetings/db/dao/room/RoomDao.java   |   7 +-
 .../apache/openmeetings/db/dao/room/SipConfig.java | 123 -------
 .../openmeetings/db/entity/basic/Client.java       |   5 +
 .../openmeetings/db/manager/IClientManager.java    |   6 +-
 .../{dao/room => manager}/IInvitationManager.java  |   2 +-
 .../{IClientManager.java => ISipManager.java}      |  20 +-
 .../openmeetings/db/util/ApplicationHelper.java    |   8 +-
 .../openmeetings/db/util/ws/RoomMessage.java       |   2 +-
 .../installation/ImportInitvalues.java             |   4 +-
 .../src/site/markdown/AsteriskIntegration.md       |   9 +-
 .../src/site/markdown/InstallMediaServer.md        |   2 +-
 openmeetings-server/src/site/site.xml              |   2 +-
 .../src/site/xdoc/PrivacyStatement.xml             |  12 +-
 .../src/site/xdoc/voip-sip-integration.xml         | 360 ---------------------
 .../service/notifier/MailNotifier.java             |   2 +-
 .../service/room/InvitationManager.java            |   2 +-
 .../{quartz => }/scheduler/AbstractJob.java        |   2 +-
 .../service/{quartz => }/scheduler/AtomReader.java |   2 +-
 .../service/{quartz => }/scheduler/CleanupJob.java |  25 +-
 .../{quartz => }/scheduler/ReminderJob.java        |   2 +-
 .../web/admin/connection/ConnectionsPanel.java     |  12 +-
 .../openmeetings/web/admin/rooms/RoomForm.java     |   3 +-
 .../apache/openmeetings/web/app/Application.java   |  10 +
 .../apache/openmeetings/web/app/ClientManager.java |  86 ++---
 .../apache/openmeetings/web/app/TimerService.java  |  61 +++-
 .../apache/openmeetings/web/app/UserManager.java   |   6 +-
 .../apache/openmeetings/web/room/RoomPanel.java    |  51 ++-
 .../web/room/menu/SipDialerDialog.java             |   4 +-
 .../web/room/wb/WbWebSocketHelper.java             |   3 +-
 .../openmeetings/web/user/MessageDialog.java       |   2 +-
 .../openmeetings/web/user/rooms/RoomListPanel.java |   4 +-
 .../openmeetings/web/user/rooms/RoomsPanel.java    |   3 +-
 .../webapp/WEB-INF/classes/applicationContext.xml  |  51 +--
 .../webapp/WEB-INF/classes/openmeetings.properties |  65 ++++
 .../openmeetings/service/quartz/TestJob.java       |   4 +-
 .../openmeetings/webservice/RoomWebService.java    |  12 +-
 pom.xml                                            |  37 ++-
 45 files changed, 433 insertions(+), 843 deletions(-)

diff --git a/openmeetings-core/pom.xml b/openmeetings-core/pom.xml
index dd82ded..936ee4c 100644
--- a/openmeetings-core/pom.xml
+++ b/openmeetings-core/pom.xml
@@ -105,6 +105,14 @@
                        <groupId>org.kurento</groupId>
                        <artifactId>kurento-client</artifactId>
                </dependency>
+               <dependency>
+                       <groupId>org.asteriskjava</groupId>
+                       <artifactId>asterisk-java</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>javax.sip</groupId>
+                       <artifactId>jain-sip-ri</artifactId>
+               </dependency>
                <!-- Test dependencies -->
                <dependency>
                        <groupId>org.junit.jupiter</groupId>
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
index 0aa2934..5f1214e 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
@@ -71,10 +71,13 @@ import 
org.kurento.jsonrpc.client.JsonRpcClientNettyWebSocket;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
 
 import com.github.openjson.JSONArray;
 import com.github.openjson.JSONObject;
 
+@Component
 public class KurentoHandler {
        private static final Logger log = 
LoggerFactory.getLogger(KurentoHandler.class);
        public static final String PARAM_ICE = "iceServers";
@@ -88,20 +91,30 @@ public class KurentoHandler {
        private final ScheduledExecutorService kmsRecheckScheduler = 
Executors.newScheduledThreadPool(1);
        public static final String KURENTO_TYPE = "kurento";
        private static int FLOWOUT_TIMEOUT_SEC = 5;
-       private long checkTimeout = 120000; //ms
-       private long objCheckTimeout = 200; //ms
-       private int watchThreadCount = 10;
+       @Value("${kurento.ws.url}")
        private String kurentoWsUrl;
+       @Value("${kurento.turn.url}")
        private String turnUrl;
+       @Value("${kurento.turn.user}")
        private String turnUser;
+       @Value("${kurento.turn.secret}")
        private String turnSecret;
+       @Value("${kurento.turn.mode}")
        private String turnMode;
+       @Value("${kurento.turn.ttl}")
        private int turnTtl = 60; //minutes
+       @Value("${kurento.check.timeout}")
+       private long checkTimeout = 120000; //ms
+       @Value("${kurento.object.check.timeout}")
+       private long objCheckTimeout = 200; //ms
+       @Value("${kurento.watch.thread.count}")
+       private int watchThreadCount = 10;
+       @Value("${kurento.kuid}")
+       private String kuid;
        private KurentoClient client;
        private final AtomicBoolean connected = new AtomicBoolean(false);
-       private String kuid;
-       private final Set<String> ignoredKuids = new HashSet<>();
        private final Map<Long, KRoom> rooms = new ConcurrentHashMap<>();
+       private final Set<String> ignoredKuids = new HashSet<>();
        private Runnable check;
 
        @Autowired
@@ -379,56 +392,6 @@ public class KurentoHandler {
                return kuid;
        }
 
-       public void setKuid(String kuid) {
-               this.kuid = kuid;
-       }
-
-       public void setIgnoredKuids(String ignoredKuids) {
-               if (!Strings.isEmpty(ignoredKuids)) {
-                       this.ignoredKuids.addAll(List.of(ignoredKuids.split("[, 
]")));
-               }
-       }
-
-       public void setCheckTimeout(long checkTimeout) {
-               this.checkTimeout = checkTimeout;
-       }
-
-       public void setObjCheckTimeout(long objCheckTimeout) {
-               this.objCheckTimeout = objCheckTimeout;
-       }
-
-       public void setWatchThreadCount(int watchThreadCount) {
-               this.watchThreadCount = watchThreadCount;
-       }
-
-       public void setKurentoWsUrl(String kurentoWsUrl) {
-               this.kurentoWsUrl = kurentoWsUrl;
-       }
-
-       public void setTurnUrl(String turnUrl) {
-               this.turnUrl = turnUrl;
-       }
-
-       public void setTurnUser(String turnUser) {
-               this.turnUser = turnUser;
-       }
-
-       public void setTurnSecret(String turnSecret) {
-               this.turnSecret = turnSecret;
-       }
-
-       public void setTurnMode(String turnMode) {
-               this.turnMode = turnMode;
-       }
-
-       public void setTurnTtl(int turnTtl) {
-               this.turnTtl = turnTtl;
-       }
-
-       public void setFlowoutTimeout(int timeout) {
-               FLOWOUT_TIMEOUT_SEC = timeout;
-       }
-
        IApplication getApp() {
                return app;
        }
@@ -445,6 +408,18 @@ public class KurentoHandler {
                return FLOWOUT_TIMEOUT_SEC;
        }
 
+       @Value("${kurento.flowout.timeout}")
+       private void setFlowoutTimeout(int timeout) {
+               FLOWOUT_TIMEOUT_SEC = timeout;
+       }
+
+       @Value("${kurento.ignored.kuids}")
+       private void setIgnoredKuids(String ignoredKuids) {
+               if (!Strings.isEmpty(ignoredKuids)) {
+                       this.ignoredKuids.addAll(List.of(ignoredKuids.split("[, 
]")));
+               }
+       }
+
        private class KWatchDogCreate implements 
EventListener<ObjectCreatedEvent> {
                private ScheduledExecutorService scheduler;
 
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/StreamProcessor.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/StreamProcessor.java
index 7abacce..27fbe41 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/StreamProcessor.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/StreamProcessor.java
@@ -306,10 +306,12 @@ public class StreamProcessor implements IStreamProcessor {
                }
                KRoom room = kHandler.getRoom(roomId);
                if (room.isSharing()) {
-                       List<StreamDesc> streams = 
cm.listByRoom(roomId).parallelStream()
+                       if (cm.streamByRoom(roomId)
                                        .flatMap(c -> c.getStreams().stream())
-                                       .filter(sd -> StreamType.SCREEN == 
sd.getType()).collect(Collectors.toList());
-                       if (streams.isEmpty()) {
+                                       .filter(sd -> StreamType.SCREEN == 
sd.getType())
+                                       .findAny()
+                                       .isEmpty())
+                       {
                                log.info("No more screen streams in the room, 
stopping sharing");
                                room.stopSharing();
                                if (Room.Type.INTERVIEW != room.getType() && 
room.isRecording()) {
@@ -319,10 +321,11 @@ public class StreamProcessor implements IStreamProcessor {
                        }
                }
                if (room.isRecording()) {
-                       List<StreamDesc> streams = 
cm.listByRoom(roomId).parallelStream()
+                       if (cm.streamByRoom(roomId)
                                        .flatMap(c -> c.getStreams().stream())
-                                       .collect(Collectors.toList());
-                       if (streams.isEmpty()) {
+                                       .findAny()
+                                       .isEmpty())
+                       {
                                log.info("No more streams in the room, stopping 
recording");
                                room.stopRecording(null);
                        }
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipDao.java 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/sip/SipManager.java
similarity index 84%
rename from 
openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipDao.java
rename to 
openmeetings-core/src/main/java/org/apache/openmeetings/core/sip/SipManager.java
index 5f8b2f5..5ffa17a 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipDao.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/sip/SipManager.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.db.dao.room;
+package org.apache.openmeetings.core.sip;
 
 import static javax.sip.message.Request.INVITE;
 import static javax.sip.message.Request.REGISTER;
@@ -24,6 +24,8 @@ import static javax.sip.message.Response.OK;
 import static javax.sip.message.Response.RINGING;
 import static javax.sip.message.Response.TRYING;
 import static javax.sip.message.Response.UNAUTHORIZED;
+import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.isSipEnabled;
 
 import java.text.ParseException;
 import java.util.List;
@@ -31,6 +33,7 @@ import java.util.Properties;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 import javax.annotation.PostConstruct;
 import javax.sip.ClientTransaction;
@@ -54,6 +57,9 @@ import javax.sip.message.Request;
 import javax.sip.message.Response;
 
 import org.apache.openmeetings.db.entity.room.Room;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.ISipManager;
+import org.apache.openmeetings.util.OmFileHelper;
 import org.asteriskjava.manager.DefaultManagerConnection;
 import org.asteriskjava.manager.ManagerConnection;
 import org.asteriskjava.manager.ManagerConnectionFactory;
@@ -70,7 +76,7 @@ import org.asteriskjava.manager.response.ManagerError;
 import org.asteriskjava.manager.response.ManagerResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import gov.nist.javax.sip.DialogTimeoutEvent;
@@ -82,8 +88,8 @@ import gov.nist.javax.sip.clientauthutils.UserCredentials;
 import gov.nist.javax.sip.stack.NioMessageProcessorFactory;
 
 @Service
-public class SipDao implements SipListenerExt {
-       private static final Logger log = LoggerFactory.getLogger(SipDao.class);
+public class SipManager implements ISipManager, SipListenerExt {
+       private static final Logger log = 
LoggerFactory.getLogger(SipManager.class);
        public static final String ASTERISK_OM_FAMILY = "openmeetings";
        public static final String ASTERISK_OM_KEY = "rooms";
        public static final String SIP_FIRST_NAME = "SIP Transport";
@@ -93,6 +99,28 @@ public class SipDao implements SipListenerExt {
                return t -> {};
        }
 
+       @Value("${sip.hostname}")
+       private String sipHostname;
+       @Value("${sip.manager.port}")
+       private int managerPort;
+       @Value("${sip.manager.user}")
+       private String managerUser;
+       @Value("${sip.manager.password}")
+       private String managerPass;
+       @Value("${sip.manager.timeout}")
+       private long managerTimeout;
+
+       @Value("${sip.ws.local.port}")
+       private int localWsPort = 6666;
+       @Value("${sip.ws.local.host}")
+       private String localWsHost;
+       @Value("${sip.ws.remote.port}")
+       private int wsPort;
+       @Value("${sip.ws.remote.user}")
+       private String omSipUser;
+       @Value("${sip.ws.remote.password}")
+       private String omSipPasswd;
+
        private final AtomicLong cseq = new AtomicLong();
        private final Random rnd = new Random();
 
@@ -106,18 +134,16 @@ public class SipDao implements SipListenerExt {
        private AddressFactory addressFactory;
        private ContactHeader contactHeader;
        private ManagerConnectionFactory factory;
-
-       @Autowired
-       private SipConfig config;
+       private String sipUserPicture;
 
        @PostConstruct
        public void init() throws Exception {
-               if (config.getSipHostname() != null) {
+               if (sipHostname != null) {
                        factory = new ManagerConnectionFactory(
-                                       config.getSipHostname()
-                                       , config.getManagerPort()
-                                       , config.getManagerUser()
-                                       , config.getManagerPass());
+                                       sipHostname
+                                       , managerPort
+                                       , managerUser
+                                       , managerPass);
                        final SipFactory sipFactory = SipFactory.getInstance();
                        sipFactory.setPathName("gov.nist");
 
@@ -134,14 +160,14 @@ public class SipDao implements SipListenerExt {
                        headerFactory = sipFactory.createHeaderFactory();
                        addressFactory = sipFactory.createAddressFactory();
                        final ListeningPoint listeningPoint = 
sipStack.createListeningPoint(
-                                       config.getLocalWsHost()
-                                       , config.getLocalWsPort()
+                                       localWsHost
+                                       , localWsPort
                                        , SIP_TRANSPORT);
                        sipProvider = 
sipStack.createSipProvider(listeningPoint);
                        sipProvider.addSipListener(this);
-                       Address contact = createAddr(config.getOmSipUser(), 
config.getLocalWsHost(), uri -> {
+                       Address contact = createAddr(omSipUser, localWsHost, 
uri -> {
                                try {
-                                       uri.setPort(config.getLocalWsPort());
+                                       uri.setPort(localWsPort);
                                        uri.setTransportParam(SIP_TRANSPORT);
                                } catch (ParseException e) {
                                        log.error("fail to create contact 
address", e);
@@ -153,10 +179,10 @@ public class SipDao implements SipListenerExt {
 
        private ManagerConnection getConnection() {
                DefaultManagerConnection con = 
(DefaultManagerConnection)factory.createManagerConnection();
-               con.setDefaultEventTimeout(config.getManagerTimeout());
-               con.setDefaultResponseTimeout(config.getManagerTimeout());
-               con.setSocketReadTimeout((int)config.getManagerTimeout());
-               con.setSocketTimeout((int)config.getManagerTimeout());
+               con.setDefaultEventTimeout(managerTimeout);
+               con.setDefaultResponseTimeout(managerTimeout);
+               con.setSocketReadTimeout((int)managerTimeout);
+               con.setSocketTimeout((int)managerTimeout);
                return con;
        }
 
@@ -228,6 +254,7 @@ public class SipDao implements SipListenerExt {
                return pin;
        }
 
+       @Override
        public void update(String confno, String pin) {
                delete(confno);
                DbPutAction da = new DbPutAction(ASTERISK_OM_FAMILY, 
getKey(confno), pin);
@@ -239,6 +266,7 @@ public class SipDao implements SipListenerExt {
                exec(da);
        }
 
+       @Override
        public void delete(String confno) {
                DbDelAction da = new DbDelAction(ASTERISK_OM_FAMILY, 
getKey(confno));
                exec(da);
@@ -251,7 +279,7 @@ public class SipDao implements SipListenerExt {
                ConfbridgeListAction da = new ConfbridgeListAction(confno);
                ResponseEvents r = execEvent(da);
                if (r != null) {
-                       log.debug("SipDao::countUsers size == {}", 
r.getEvents().size());
+                       log.debug("SipManager::countUsers size == {}", 
r.getEvents().size());
                        // "- 1" here means: ListComplete event
                        return r.getEvents().size() - 1;
                }
@@ -278,11 +306,29 @@ public class SipDao implements SipListenerExt {
                oa.setContext("rooms-out");
                oa.setExten(number);
                oa.setPriority(1);
-               oa.setTimeout(config.getManagerTimeout());
+               oa.setTimeout(managerTimeout);
 
                exec(oa);
        }
 
+       public void setUserPicture(Function<User, String> pictureCreator) {
+               User u = new User();
+               u.setId(SIP_USER_ID);
+               sipUserPicture = pictureCreator.apply(u);
+       }
+
+       public User getSipUser(Room r) {
+               if (factory == null || !isSipEnabled() || !r.isSipEnabled()) {
+                       return null;
+               }
+               User u = new User();
+               u.setId(OmFileHelper.SIP_USER_ID);
+               u.setFirstname(SIP_FIRST_NAME);
+               u.setLogin(SIP_USER_NAME);
+               u.setPictureUri(sipUserPicture);
+               return u;
+       }
+
        @Override
        public void processDialogTerminated(DialogTerminatedEvent evt) {
                log.error("processDialogTerminated: \n{}", evt);
@@ -355,7 +401,7 @@ public class SipDao implements SipListenerExt {
        }
 
        private Address createAddr(String user) {
-               return createAddr(user, config.getSipHostname(), noop());
+               return createAddr(user, sipHostname, noop());
        }
 
        private Address createAddr(String user, String host, Consumer<SipUri> 
cons) {
@@ -373,11 +419,11 @@ public class SipDao implements SipListenerExt {
 
        private void sendRequest(String method, String to, Consumer<SipUri> 
uriCons, Consumer<Request> reqCons) throws Exception {
                SipUri uri = new SipUri();
-               uri.setHost(config.getSipHostname());
-               uri.setPort(config.getWsPort());
+               uri.setHost(sipHostname);
+               uri.setPort(wsPort);
                uri.setTransportParam(SIP_TRANSPORT);
                uri.setMethodParam("GET");
-               uri.setHeader("Host", config.getSipHostname());
+               uri.setHeader("Host", sipHostname);
                uri.setHeader("Location", "/ws");
                uriCons.accept(uri);
 
@@ -386,9 +432,9 @@ public class SipDao implements SipListenerExt {
                                , method
                                , sipProvider.getNewCallId()
                                , 
headerFactory.createCSeqHeader(cseq.incrementAndGet(), method)
-                               , 
headerFactory.createFromHeader(createAddr(config.getOmSipUser()), tag)
+                               , 
headerFactory.createFromHeader(createAddr(omSipUser), tag)
                                , headerFactory.createToHeader(createAddr(to), 
null)
-                               , 
List.of(headerFactory.createViaHeader(config.getLocalWsHost(), 
config.getLocalWsPort(), SIP_TRANSPORT, branch))
+                               , 
List.of(headerFactory.createViaHeader(localWsHost, localWsPort, SIP_TRANSPORT, 
branch))
                                , headerFactory.createMaxForwardsHeader(70));
                request.addHeader(contactHeader);
                request.addHeader(headerFactory.createExpiresHeader(600));
@@ -407,12 +453,12 @@ public class SipDao implements SipListenerExt {
                AuthenticationHelper helper = 
sipStack.getAuthenticationHelper((trans, s) -> new UserCredentials() {
                        @Override
                        public String getUserName() {
-                               return config.getOmSipUser();
+                               return omSipUser;
                        }
 
                        @Override
                        public String getPassword() {
-                               return config.getOmSipPasswd();
+                               return omSipPasswd;
                        }
 
                        @Override
@@ -435,7 +481,7 @@ public class SipDao implements SipListenerExt {
        private void register() throws Exception {
                sendRequest(
                                REGISTER
-                               , config.getOmSipUser()
+                               , omSipUser
                                , noop()
                                , req -> {
                                        try {
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
index 61d480f..9f733f2 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.core.util;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.alwaysTrue;
 import static org.apache.openmeetings.core.util.WebSocketHelper.doSend;
 import static org.apache.openmeetings.core.util.WebSocketHelper.publish;
 
@@ -148,7 +149,7 @@ public class ChatWebSocketHelper {
                if (publish) {
                        publish(new WsMessageChat2All(m, msg));
                }
-               WebSocketHelper.send(a -> 
((IApplication)a).getBean(IClientManager.class).list()
-                               , (t, c) -> doSend(t, c, msg, (o, cm) -> 
setDates(o, m, c.getUser(), false), "all"), null);
+               WebSocketHelper.send(a -> 
((IApplication)a).getBean(IClientManager.class).stream()
+                               , (t, c) -> doSend(t, c, msg, (o, cm) -> 
setDates(o, m, c.getUser(), false), "all"), alwaysTrue());
        }
 }
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
index 10a2de7..16c3698 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
@@ -21,12 +21,12 @@ package org.apache.openmeetings.core.util;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.getWicketApplicationName;
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.util.ws.WsMessageAll;
@@ -54,6 +54,9 @@ import com.github.openjson.JSONObject;
 
 public class WebSocketHelper {
        private static final Logger log = 
LoggerFactory.getLogger(WebSocketHelper.class);
+       public static final <T> Predicate<T> alwaysTrue() {
+               return x -> true;
+       }
 
        private WebSocketHelper() {
                // denied
@@ -139,7 +142,7 @@ public class WebSocketHelper {
                        publish(new WsMessageRoomMsg(m));
                }
                log.trace("Sending WebSocket message to room: {} {}", 
m.getType(), m instanceof TextRoomMessage ? ((TextRoomMessage)m).getText() : 
"");
-               sendRoom(m.getRoomId(), (t, c) -> t.sendMessage(m), null);
+               sendRoom(m.getRoomId(), (t, c) -> t.sendMessage(m), 
alwaysTrue());
        }
 
        public static void sendServer(final RoomMessage m) {
@@ -161,7 +164,7 @@ public class WebSocketHelper {
                if (publish) {
                        publish(new WsMessageRoom(roomId, m));
                }
-               sendRoom(roomId, m, null, null);
+               sendRoom(roomId, m, alwaysTrue(), null);
        }
 
        public static void sendRoomOthers(final Long roomId, final String uid, 
final JSONObject m) {
@@ -183,8 +186,8 @@ public class WebSocketHelper {
                if (publish) {
                        publish(new WsMessageUser(userId, m));
                }
-               send(a -> 
((IApplication)a).getBean(IClientManager.class).listByUser(userId)
-                               , (t, c) -> doSend(t, c, m, func, "user"), 
null);
+               send(a -> 
((IApplication)a).getBean(IClientManager.class).listByUser(userId).stream()
+                               , (t, c) -> doSend(t, c, m, func, "user"), 
alwaysTrue());
        }
 
        public static void sendAll(final String m) {
@@ -239,11 +242,11 @@ public class WebSocketHelper {
        }
 
        private static void sendRoom(final Long roomId, 
BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
-               send(a -> 
((IApplication)a).getBean(IClientManager.class).listByRoom(roomId), consumer, 
check);
+               send(a -> 
((IApplication)a).getBean(IClientManager.class).streamByRoom(roomId), consumer, 
check);
        }
 
        static void send(
-                       final Function<Application, Collection<Client>> func
+                       final Function<Application, Stream<Client>> func
                        , BiConsumer<IWebSocketConnection, Client> consumer
                        , Predicate<Client> check)
        {
@@ -255,14 +258,14 @@ public class WebSocketHelper {
                        WebSocketSettings settings = 
WebSocketSettings.Holder.get(app);
                        IWebSocketConnectionRegistry reg = 
settings.getConnectionRegistry();
                        Executor executor = 
settings.getWebSocketPushMessageExecutor();
-                       for (Client c : func.apply(app)) {
-                               if (check == null || check.test(c)) {
-                                       final IWebSocketConnection wc = 
reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
-                                       if (wc != null && wc.isOpen()) {
-                                               executor.run(() -> 
consumer.accept(wc, c));
-                                       }
-                               }
-                       }
+                       func.apply(app)
+                                       .filter(check)
+                                       .forEach(c -> {
+                                               final IWebSocketConnection wc = 
reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
+                                               if (wc != null && wc.isOpen()) {
+                                                       executor.run(() -> 
consumer.accept(wc, c));
+                                               }
+                                       });
                }).start();
        }
 }
diff --git a/openmeetings-db/pom.xml b/openmeetings-db/pom.xml
index cd6d82a..16562ac 100644
--- a/openmeetings-db/pom.xml
+++ b/openmeetings-db/pom.xml
@@ -73,11 +73,6 @@
                        <version>${spring.version}</version>
                </dependency>
                <dependency>
-                       <groupId>org.asteriskjava</groupId>
-                       <artifactId>asterisk-java</artifactId>
-                       <version>${asterisk-java.version}</version>
-               </dependency>
-               <dependency>
                        <groupId>org.apache.commons</groupId>
                        <artifactId>commons-dbcp2</artifactId>
                        <version>${commons-dbcp.version}</version>
@@ -109,10 +104,6 @@
                        <version>${mssql.version}</version>
                </dependency>
                <dependency>
-                       <groupId>javax.sip</groupId>
-                       <artifactId>jain-sip-ri</artifactId>
-               </dependency>
-               <dependency>
                        <groupId>org.apache.openmeetings</groupId>
                        <artifactId>openmeetings-util</artifactId>
                        <version>${project.version}</version>
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
index 988bc2e..d5b52f8 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/calendar/AppointmentDao.java
@@ -37,13 +37,13 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
-import org.apache.openmeetings.db.dao.room.IInvitationManager;
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dto.calendar.AppointmentDTO;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
 import org.apache.openmeetings.db.entity.calendar.Appointment.Reminder;
 import org.apache.openmeetings.db.entity.calendar.MeetingMember;
 import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
+import org.apache.openmeetings.db.manager.IInvitationManager;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
index 02b84f5..696959b 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/RoomDao.java
@@ -46,6 +46,7 @@ import org.apache.openmeetings.db.entity.log.ConferenceLog;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.room.Room.Type;
+import org.apache.openmeetings.db.manager.ISipManager;
 import org.apache.openmeetings.db.entity.room.RoomFile;
 import org.apache.openmeetings.db.entity.room.RoomGroup;
 import org.apache.openmeetings.db.util.DaoHelper;
@@ -70,7 +71,7 @@ public class RoomDao implements 
IGroupAdminDataProviderDao<Room> {
        @Autowired
        private ConfigurationDao cfgDao;
        @Autowired
-       private SipDao sipDao;
+       private ISipManager sipManager;
        @Autowired
        private UserDao userDao;
 
@@ -213,9 +214,9 @@ public class RoomDao implements 
IGroupAdminDataProviderDao<Room> {
                        if (sipNumber != null && 
!sipNumber.equals(entity.getConfno())) {
                                entity.setConfno(sipNumber);
                        }
-                       sipDao.update(sipNumber, entity.getPin());
+                       sipManager.update(sipNumber, entity.getPin());
                } else {
-                       sipDao.delete(entity.getConfno());
+                       sipManager.delete(entity.getConfno());
                        entity.setConfno(null);
                        entity.setPin(null);
                }
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipConfig.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipConfig.java
deleted file mode 100644
index e1a5646..0000000
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/SipConfig.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License") +  you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.openmeetings.db.dao.room;
-
-public class SipConfig {
-       private String sipHostname;
-       private int managerPort;
-       private String managerUser;
-       private String managerPass;
-       private long managerTimeout;
-
-       private int localWsPort = 6666;
-       private String localWsHost;
-       private int wsPort;
-       private String omSipUser;
-       private String omSipPasswd;
-
-       private String uid; //FIXME TODO is this still required ?!
-
-       public String getSipHostname() {
-               return sipHostname;
-       }
-
-       public void setSipHostname(String sipHostname) {
-               this.sipHostname = sipHostname;
-       }
-
-       public int getManagerPort() {
-               return managerPort;
-       }
-
-       public void setManagerPort(int managerPort) {
-               this.managerPort = managerPort;
-       }
-
-       public String getManagerUser() {
-               return managerUser;
-       }
-
-       public void setManagerUser(String managerUser) {
-               this.managerUser = managerUser;
-       }
-
-       public String getManagerPass() {
-               return managerPass;
-       }
-
-       public void setManagerPass(String managerPass) {
-               this.managerPass = managerPass;
-       }
-
-       public long getManagerTimeout() {
-               return managerTimeout;
-       }
-
-       public void setManagerTimeout(long managerTimeout) {
-               this.managerTimeout = managerTimeout;
-       }
-
-       public int getLocalWsPort() {
-               return localWsPort;
-       }
-
-       public void setLocalWsPort(int localWsPort) {
-               this.localWsPort = localWsPort;
-       }
-
-       public String getLocalWsHost() {
-               return localWsHost;
-       }
-
-       public void setLocalWsHost(String localWsHost) {
-               this.localWsHost = localWsHost;
-       }
-
-       public int getWsPort() {
-               return wsPort;
-       }
-
-       public void setWsPort(int wsPort) {
-               this.wsPort = wsPort;
-       }
-
-       public String getOmSipUser() {
-               return omSipUser;
-       }
-
-       public void setOmSipUser(String omSipUser) {
-               this.omSipUser = omSipUser;
-       }
-
-       public String getOmSipPasswd() {
-               return omSipPasswd;
-       }
-
-       public void setOmSipPasswd(String omSipPasswd) {
-               this.omSipPasswd = omSipPasswd;
-       }
-
-       public String getUid() {
-               return uid;
-       }
-
-       public void setUid(String uid) {
-               this.uid = uid;
-       }
-}
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
index cdf463b..0304452 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.db.entity.basic;
 
 import static java.util.UUID.randomUUID;
+import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
 
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -129,6 +130,10 @@ public class Client implements IDataProviderEntity, 
IWsClient {
                return sid;
        }
 
+       public boolean isSip() {
+               return SIP_USER_ID.equals(getUserId());
+       }
+
        public void clear() {
                activities.clear();
                rights.clear();
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
index 24a1498..0e894da 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.db.manager;
 
 import java.util.Collection;
-import java.util.List;
+import java.util.stream.Stream;
 
 import org.apache.openmeetings.db.entity.basic.Client;
 
@@ -27,8 +27,8 @@ public interface IClientManager {
        Client get(String uid);
        Client getBySid(String sid);
        String uidBySid(String sid);
-       List<Client> list();
-       List<Client> listByRoom(Long roomId);
+       Stream<Client> stream();
+       Stream<Client> streamByRoom(Long roomId);
        Collection<Client> listByUser(Long userId);
        Client update(Client c);
        void exit(Client c);
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/IInvitationManager.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IInvitationManager.java
similarity index 97%
rename from 
openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/IInvitationManager.java
rename to 
openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IInvitationManager.java
index 2fd0c34..44f3f65 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/room/IInvitationManager.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IInvitationManager.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.db.dao.room;
+package org.apache.openmeetings.db.manager;
 
 import java.util.Date;
 
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/ISipManager.java
similarity index 69%
copy from 
openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
copy to 
openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/ISipManager.java
index 24a1498..b94263a 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/ISipManager.java
@@ -18,18 +18,12 @@
  */
 package org.apache.openmeetings.db.manager;
 
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.openmeetings.db.entity.basic.Client;
+/**
+ * this interface is required to use SipMagager from openmeetings-core
+ */
+public interface ISipManager {
+       public static final String SIP_FIRST_NAME = "SIP Transport";
 
-public interface IClientManager {
-       Client get(String uid);
-       Client getBySid(String sid);
-       String uidBySid(String sid);
-       List<Client> list();
-       List<Client> listByRoom(Long roomId);
-       Collection<Client> listByUser(Long userId);
-       Client update(Client c);
-       void exit(Client c);
+       void update(String confno, String pin);
+       void delete(String confno);
 }
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ApplicationHelper.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ApplicationHelper.java
index 38ef520..b8d7919 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ApplicationHelper.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ApplicationHelper.java
@@ -116,13 +116,17 @@ public class ApplicationHelper {
                }
        }
 
-       public static IApplication ensureApplication(Long langId) {
-               IApplication a = ensureApplication();
+       public static void ensureRequestCycle(IApplication a) {
                if (ThreadContext.getRequestCycle() == null) {
                        ServletWebRequest req = new ServletWebRequest(new 
MockHttpServletRequest((Application)a, new 
MockHttpSession(a.getServletContext()), a.getServletContext()), "");
                        RequestCycleContext rctx = new RequestCycleContext(req, 
new MockWebResponse(), a.getRootRequestMapper(), 
a.getExceptionMapperProvider().get());
                        ThreadContext.setRequestCycle(new RequestCycle(rctx));
                }
+       }
+
+       public static IApplication ensureApplication(Long langId) {
+               IApplication a = ensureApplication();
+               ensureRequestCycle(a);
                if (ThreadContext.getSession() == null) {
                        WebSession s = WebSession.get();
                        if (langId > 0) {
diff --git 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
index 85937b7..9864bc2 100644
--- 
a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
+++ 
b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.db.util.ws;
 
 import static java.util.UUID.randomUUID;
-import static org.apache.openmeetings.db.dao.room.SipDao.SIP_FIRST_NAME;
+import static org.apache.openmeetings.db.manager.ISipManager.SIP_FIRST_NAME;
 import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
 
 import java.util.Date;
diff --git 
a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
 
b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
index 718078e..f987e1d 100644
--- 
a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
+++ 
b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
@@ -125,10 +125,10 @@ import java.util.Date;
 import java.util.List;
 import java.util.function.Consumer;
 
+import org.apache.openmeetings.core.sip.SipManager;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.room.SipDao;
 import org.apache.openmeetings.db.dao.server.OAuth2Dao;
 import org.apache.openmeetings.db.dao.user.GroupDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -170,7 +170,7 @@ public class ImportInitvalues {
        @Autowired
        private UserDao userDao;
        @Autowired
-       private SipDao sipDao;
+       private SipManager sipDao;
        @Autowired
        private OAuth2Dao oauthDao;
        @Autowired
diff --git a/openmeetings-server/src/site/markdown/AsteriskIntegration.md 
b/openmeetings-server/src/site/markdown/AsteriskIntegration.md
index 2fd9105..554584f 100644
--- a/openmeetings-server/src/site/markdown/AsteriskIntegration.md
+++ b/openmeetings-server/src/site/markdown/AsteriskIntegration.md
@@ -165,7 +165,7 @@ Modify `/etc/asterisk/extensions.conf`
 ; If you do not receive an output with that resembles openmeetings/rooms/400## 
where “##” will equal
 ; the extension assigned when you created your room
 ; If you do not receive the above output check your parameters in
-; /opt/om/webapps/openmeetings/WEB-INF/classes/applicationContext.xml
+; /opt/om/webapps/openmeetings/WEB-INF/classes/openmeetings.properties
 ; Go back into the Administrator Panel and remove the PIN number in each room 
save the record with
 ; no PIN number and then re-enter the pin again resave the record.
 ; *****************************************************
@@ -246,12 +246,9 @@ write = all
 ```
 
 Update OpenMeetings with credentials for Asterisk manager.
-Modify `/opt/om/webapps/openmeetings/WEB-INF/classes/applicationContext.xml`
+Modify `/opt/om/webapps/openmeetings/WEB-INF/classes/openmeetings.properties`
 
-find **&lt;bean class="org.apache.openmeetings.db.dao.room.SipConfig"&gt;**
-uncomment its properties and set it to your custom values.
-
-set value for `uid` property to unique secret value (can be generated here <a 
href="https://www.uuidtools.com";>https://www.uuidtools.com</a>)
+find all properties start with `sip.` and set it to your custom values.
 
 <p style="font-size: larger; color: blue;">
        IMPORTANT: this step should be done <strong>BEFORE</strong> system 
install/restore
diff --git a/openmeetings-server/src/site/markdown/InstallMediaServer.md 
b/openmeetings-server/src/site/markdown/InstallMediaServer.md
index 3034ca7..8ffc72e 100644
--- a/openmeetings-server/src/site/markdown/InstallMediaServer.md
+++ b/openmeetings-server/src/site/markdown/InstallMediaServer.md
@@ -13,4 +13,4 @@ Licensed under the Apache License, Version 2.0 (the 
"License") http://www.apache
 
 ## Specify/Install Turn server
 
-<div class="bd-callout bd-callout-info">Optional step</div>
+<div class="bd-callout bd-callout-warning">Only local installation will work 
without TURN server</div>
diff --git a/openmeetings-server/src/site/site.xml 
b/openmeetings-server/src/site/site.xml
index ef599b9..5a27a60 100644
--- a/openmeetings-server/src/site/site.xml
+++ b/openmeetings-server/src/site/site.xml
@@ -47,7 +47,7 @@
                                <item name="REST API Sample" 
href="/RestAPISample.html" />
                                <item name="Ldap and ADS" 
href="/LdapAndADS.html" />
                                <item name="OAuth2" href="/oauth2.html" />
-                               <item name="VoIP and SIP" 
href="/voip-sip-integration.html" />
+                               <item name="VoIP and SIP" 
href="/AsteriskIntegration.html" />
                                <item name="Errors table" 
href="/errorvalues.html" />
                                <item name="CalDAV and Google Calendar 
integration" href="/CalDAVandGCal.html" />
                                <item name="External Video/Camera" 
href="/ExternalVideo.html" />
diff --git a/openmeetings-server/src/site/xdoc/PrivacyStatement.xml 
b/openmeetings-server/src/site/xdoc/PrivacyStatement.xml
index ad91620..319f18b 100644
--- a/openmeetings-server/src/site/xdoc/PrivacyStatement.xml
+++ b/openmeetings-server/src/site/xdoc/PrivacyStatement.xml
@@ -24,7 +24,7 @@
                        <div>
                                To modify privacy statement do the following:
                                <ul>
-                                       <li>Open 
<pp>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage.html</pp></li>
+                                       <li>Open 
<tt>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage.html</tt></li>
                                        <li>Perform necessary changes</li>
                                        <li>restart OM</li>
                                </ul>
@@ -35,11 +35,11 @@
                                To create privacy statement in your language do 
the following:
                                <ul>
                                        <li>
-                                               Create new file 
<pp>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_<b>LANG_CODE</b>.html</pp>
 <br/>
-                                               <pp>LANG_CODE</pp> should be in 
format [language code]_[country ISO code], <pp>_[country ISO code]</pp> block 
is optional<br/>
-                                               for ex. <pp>de</pp> can be 
suffix for German, file name will be 
<pp>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_de.html</pp><br/>
-                                               <pp>pt</pp> can be suffix for 
Portuguese, file name will be 
<pp>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_pt.html</pp><br/>
-                                               <pp>pt_BR</pp> can be suffix 
for Brazilian version of Portuguese, file name will be 
<pp>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_pt_BR.html</pp><br/>
+                                               Create new file 
<tt>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_<b>LANG_CODE</b>.html</tt>
 <br/>
+                                               <tt>LANG_CODE</tt> should be in 
format [language code]_[country ISO code], <tt>_[country ISO code]</tt> block 
is optional<br/>
+                                               for ex. <tt>de</tt> can be 
suffix for German, file name will be 
<tt>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_de.html</tt><br/>
+                                               <tt>pt</tt> can be suffix for 
Portuguese, file name will be 
<tt>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_pt.html</tt><br/>
+                                               <tt>pt_BR</tt> can be suffix 
for Brazilian version of Portuguese, file name will be 
<tt>webapps/openmeetings/WEB-INF/classes/org/apache/openmeetings/web/pages/PrivacyPage_pt_BR.html</tt><br/>
                                        </li>
                                        <li>Perform translation</li>
                                        <li>restart OM</li>
diff --git a/openmeetings-server/src/site/xdoc/voip-sip-integration.xml 
b/openmeetings-server/src/site/xdoc/voip-sip-integration.xml
deleted file mode 100644
index 85bef4b..0000000
--- a/openmeetings-server/src/site/xdoc/voip-sip-integration.xml
+++ /dev/null
@@ -1,360 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
- -->
-<document xmlns="http://maven.apache.org/XDOC/2.0";
-               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-               xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 
http://maven.apache.org/xsd/xdoc-2.0.xsd";>
-       <properties>
-               <title>VoIP and SIP Integration</title>
-               <author email="d...@openmeetings.apache.org">Apache 
OpenMeetings Team</author>
-       </properties>
-       <body>
-               <section name="Not implemented">
-                       <div class="bd-callout bd-callout-danger">
-                               Please NOTE: this functionality is not yet 
implemented in 5.0.x
-                       </div>
-               </section>
-
-               <section name="VoIP and SIP Integration">
-                       <p>
-                               There are multiple ways to integrate with VoIP 
and or SIP.
-                               OpenMeetings does not provide out of the box a 
ready to run VoIP
-                               integration / integration to cell phone or 
usual land lane.
-                               The
-                               nature of such integrations is that it depends 
heavily on the
-                               infrastructure that you are using and where you 
would like to
-                               integrate OpenMeetings into.
-                               <br />
-                               <br />
-                               It also depends on a number of factors of which 
OpenMeetings is
-                               impossible to set up for you, for example 
setting up your VoIP
-                               server or provide you with a range of telephone 
numbers reserved for
-                               conference calls in your national phone network.
-                               Such an integration
-                               project is likely to become a consulting job 
for a
-                               telecommunications consultant.
-                               <br />
-                               <br />
-                               To get help on the integration you can contact 
the
-                               <a href="mailing-lists.html">mailing lists</a>
-                               or for example somebody from the list of
-                               <a href="commercial-support.html">commercial 
support</a>.
-                               <br/><br/>
-                       </p>
-               </section>
-               <section name="SIP-Transport Integration">
-                       <p>You need Apache OpenMeetings <strong>version 
4.0+</strong> to apply this guide!</p>
-                       <p>You need Asterisk <strong>version 13+</strong> to 
apply this guide!</p>
-                       <p>Here is instruction how-to set up red5sip transport 
integration with OpenMeetings on Ubuntu 16.04.</p>
-               </section>
-               <section name="Prerequisites">
-                       <div>
-                               Run the commands
-                               <source>
-<![CDATA[
-sudo apt update && sudo apt upgrade
-]]>
-                               </source>
-                       </div>
-               </section>
-               <section name="Building and setting up Asterisk">
-                       <div>
-                               Run the commands
-                               <source>
-<![CDATA[
-sudo mkdir /usr/src/asterisk && cd /usr/src/asterisk
-sudo wget 
http://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-13.17.0.tar.gz
-sudo tar -xvzf asterisk-13.17.0.tar.gz
-cd ./asterisk-13.17.0
-sudo make clean
-sudo contrib/scripts/install_prereq install
-sudo ./configure
-sudo make menuconfig
-]]>
-                               </source>
-                               Make sure you have selected  <tt>Add-ons -> 
res_config_mysql</tt>, Press F12 to save
-                               <source>
-<![CDATA[
-sudo make
-sudo make install
-sudo make samples
-sudo make config
-sudo service asterisk start
-]]>
-                               </source>
-                       </div>
-               </section>
-               <section name="Configure Asterisk">
-                       <div>
-                               Enable asterisk MySQL module:<br /><br />
-                               Modify "[modules]" section of 
<tt>/etc/asterisk/modules.conf</tt> as follows:<br />
-                               <strong>Add/uncomment the following 
lines</strong>
-                               <source>
-<![CDATA[
-preload => res_config_mysql.so
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Configure MySQL module:<br /><br />
-                               Set valid data for MySQL in 
<tt>/etc/asterisk/res_config_mysql.conf</tt> :<br />
-                               <strong>Example</strong>
-                               <source>
-<![CDATA[
-[general]
-dbhost = 127.0.0.1
-dbname = openmeetings
-dbuser = root
-dbpass =
-dbport = 3306
-dbsock = /var/lib/mysql/mysql.sock
-dbcharset = utf8
-requirements=warn
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Modify <tt>/etc/asterisk/sip.conf</tt><br />
-                               <strong>Add/uncomment the following 
line</strong>:<br />
-                               <source>
-<![CDATA[
-videosupport=yes
-rtcachefriends=yes
-]]>
-                               </source>
-                               <strong>Increase maxexpiry value to 
43200</strong>:<br />
-                               <source>
-<![CDATA[
-maxexpiry=43200
-]]>
-                               </source>
-                               <strong>Add user for the "SIP 
Transport"</strong>:<br />
-                               <source>
-<![CDATA[
-[red5sip_user]
-type=friend
-secret=12345
-disallow=all
-allow=ulaw
-allow=h263
-host=dynamic
-nat=force_rport,comedia
-context=rooms-red5sip
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Add next lines into the 
<tt>/etc/asterisk/extconfig.conf</tt>:
-                               <source>
-<![CDATA[
-[settings]
-sippeers => mysql,general,sipusers
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Modify 
<tt>/etc/asterisk/extensions.conf</tt><br />
-                               <strong>Add the following section</strong>:<br 
/>
-                               <source>
-<![CDATA[
-; *****************************************************
-; The below dial plan is used to dial into a Openmeetings Conference room
-; The first line DB_EXISTS(openmeetings/room/ does not belong to the 
openmeetings application
-; but is the name of astDB containing the astDB family/key pair and values
-; To Check if your astDB has been created do the following in a terminal 
window type the following:
-; asterisk –rx “database show”
-; If you do not receive an output with that resembles openmeetings/rooms/400## 
where “##” will equal
-; the extension assigned when you created your room
-; If you do not receive the above output check your parameters in
-; /opt/om/webapps/openmeetings/WEB-INF/classes/applicationContext.xml
-; Go back into the Administrator Panel and remove the PIN number in each room 
save the record with
-; no PIN number and then re-enter the pin again resave the record.
-; *****************************************************
-
-[rooms]
-exten => 
_400X!,1,GotoIf($[${DB_EXISTS(openmeetings/rooms/${EXTEN})}]?ok:notavail)
-exten => _400X!,n(ok),SET(PIN=${DB(openmeetings/rooms/${EXTEN})})
-exten => _400X!,n,Set(CONFBRIDGE(user,template)=sip_user)
-exten => _400X!,n,Set(CONFBRIDGE(user,pin)=${PIN})
-exten => _400X!,n(ok),Confbridge(${EXTEN},default_bridge,)
-exten => _400X!,n,Hangup
-exten => _400X!,n(notavail),Answer()
-exten => _400X!,n,Playback(invalid)
-exten => _400X!,n,Hangup
-
-[rooms-originate]
-exten => _400X!,1,Confbridge(${EXTEN},default_bridge,sip_user)
-exten => _400X!,n,Hangup
-
-[rooms-out]
-; *****************************************************
-; Extensions for outgoing calls from Openmeetings room.
-; *****************************************************
-
-[rooms-red5sip]
-exten => 
_400X!,1,GotoIf($[${DB_EXISTS(openmeetings/rooms/${EXTEN})}]?ok:notavail)
-exten => _400X!,n(ok),Confbridge(${EXTEN},default_bridge,red5sip_user)
-exten => _400X!,n(notavail),Hangup
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Modify 
<tt>/etc/asterisk/confbridge.conf</tt><br />
-                               <strong>Add/Modify the following 
secions</strong>:<br />
-                               <source>
-<![CDATA[
-[general]
-
-[red5sip_user]
-type=user
-marked=yes
-dsp_drop_silence=yes
-denoise=true
-
-[sip_user]
-type=user
-end_marked=yes
-wait_marked=yes
-music_on_hold_when_empty=yes
-dsp_drop_silence=yes
-denoise=true
-
-[default_bridge]
-type=bridge
-video_mode=follow_talker
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               To enable Asterisk Manager API modify 
<tt>/etc/asterisk/manager.conf</tt><br />
-                               <strong>Add/Modify the following 
sections</strong>:<br />
-                               <source>
-<![CDATA[
-[general]
-enabled = yes
-webenabled = no
-port = 5038
-bindaddr = 127.0.0.1
-
-[openmeetings]
-secret = 12345
-deny=0.0.0.0/0.0.0.0
-permit=127.0.0.1/255.255.255.0
-read = all
-write = all
-]]>
-                               </source>
-                       </div><br />
-                       <div>
-                               Update OpenMeetings with credentials for 
Asterisk manager.
-                               Modify 
<tt>/opt/om/webapps/openmeetings/WEB-INF/classes/applicationContext.xml</tt><br 
/>
-                               find <strong>&lt;bean id="sipDao" 
class="org.apache.openmeetings.db.dao.room.SipDao"&gt;</strong>
-                               uncomment its parameters and set it to your 
custom values.<br/>
-                               set value for <tt>uid</tt> property to unique 
secret value (can be generated here <a 
href="https://www.uuidtools.com";>https://www.uuidtools.com</a>)
-                               and sync it with <tt>settings.properties</tt> 
of red5sip (see below)
-                               <p style="font-size: larger; color: blue;">
-                                       IMPORTANT: this step should be done 
<strong>BEFORE</strong> system install/restore
-                                       otherwise all SIP related room 
information will be lost
-                               </p>
-                       </div><br />
-                       <div>
-                               Restart asterisk:
-                               <source>
-<![CDATA[
-service asterisk restart
-]]>
-                               </source>
-                       </div><br />
-               </section>
-
-               <section name="Setup red5sip transport">
-                       <ul>
-                               <li>Download red5sip from 
<tt>https://github.com/openmeetings/red5sip</tt>
-                                       <source>
-<![CDATA[
-git clone https://github.com/openmeetings/red5sip.git
-]]>
-                                       </source>
-                               </li>
-                               <li>Build with Apache Maven
-                                       <source>
-<![CDATA[
-cd red5sip
-mvn clean package
-]]>
-                                       </source>
-                               </li>
-                               <li>All necessary files will be available in 
<tt>target</tt> folder, copy/move it to /opt/red5sip/</li>
-                               <li>Insert proper values to the 
<tt>/opt/red5sip/settings.properties</tt>
-                                       <source>
-<![CDATA[
-red5.host=127.0.0.1 # red5 server address
-om.context=openmeetings # Openmeetings context
-red5.codec=asao
-red5.codec.rate=22 # should correlate with mic setting in Admin->Config 
`flash.mic.rate`
-sip.obproxy=127.0.0.1 # asterisk adderss
-sip.phone=red5sip_user # sip phone number
-sip.authid=red5sip_user # sip auth id
-sip.secret=12345 # sip password
-sip.realm=asterisk # sip realm
-sip.proxy=127.0.0.1 # address of sip proxy
-rooms.forceStart=no # TBD
-uid=87dddad4-9ca5-475b-860f-2e0825d02b76 #can be generated here: 
https://www.uuidtools.com/
-rooms=1 # TBD (not in use)
-]]>
-                                       </source>
-                               </li>
-                               <li>Set correct permissions on red5sip files:
-                                       <source>
-<![CDATA[
-sudo chown -R nobody:nogroup /opt/red5sip
-]]>
-                                       </source>
-                               </li>
-                               <li>Add red5sip to autostart:
-                                       <source>
-<![CDATA[
-sudo cp /opt/red5sip/red5sip /etc/init.d/
-sudo chmod a+x /etc/init.d/red5sip
-sudo update-rc.d red5sip defaults
-]]>
-                                       </source>
-                               </li>
-                               <li>Start openmeetings
-                                       <source>
-<![CDATA[
-service red5 start
-]]>
-                                       </source>
-                               </li>
-                               <li>
-                                       Enable <tt>SIP</tt> in openmeetings: 
<br/>
-                                       
<tt>Administration->Configuration->red5sip.enable == yes</tt>
-                               </li>
-                               <li>
-                                       Enable SIP for particular room(s): <br/>
-                                       <tt>Administration->Conference 
rooms->Room->Enable SIP transport in the room == checked</tt><br/>
-                                       (SIP number will be assigned to room if 
everything is OK)
-                               </li>
-                               <li>Start red5sip
-                                       <source>
-<![CDATA[
-service red5sip start
-]]>
-                                       </source>
-                               </li>
-                       </ul>
-               </section>
-       </body>
-</document>
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/notifier/MailNotifier.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/notifier/MailNotifier.java
index 3be75b2..1aa6bed 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/notifier/MailNotifier.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/notifier/MailNotifier.java
@@ -26,11 +26,11 @@ import javax.annotation.PostConstruct;
 
 import org.apache.openmeetings.core.notifier.INotifier;
 import org.apache.openmeetings.core.notifier.NotifierService;
-import org.apache.openmeetings.db.dao.room.IInvitationManager;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
 import org.apache.openmeetings.db.entity.room.Invitation;
 import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IInvitationManager;
 import 
org.apache.openmeetings.service.mail.template.subject.AppointmentReminderTemplate;
 import 
org.apache.openmeetings.service.mail.template.subject.SubjectEmailTemplate;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
index 34ae0bc..159c6ea 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
@@ -28,7 +28,6 @@ import java.util.TimeZone;
 
 import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.mail.MailHandler;
-import org.apache.openmeetings.db.dao.room.IInvitationManager;
 import org.apache.openmeetings.db.dao.room.InvitationDao;
 import org.apache.openmeetings.db.entity.basic.MailMessage;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
@@ -40,6 +39,7 @@ import 
org.apache.openmeetings.db.entity.room.Invitation.Valid;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
+import org.apache.openmeetings.db.manager.IInvitationManager;
 import org.apache.openmeetings.service.mail.template.InvitationTemplate;
 import 
org.apache.openmeetings.service.mail.template.subject.CanceledAppointmentTemplate;
 import 
org.apache.openmeetings.service.mail.template.subject.CreatedAppointmentTemplate;
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AbstractJob.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AbstractJob.java
similarity index 97%
rename from 
openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AbstractJob.java
rename to 
openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AbstractJob.java
index f1b641b..25db755 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AbstractJob.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AbstractJob.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.service.quartz.scheduler;
+package org.apache.openmeetings.service.scheduler;
 
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.isInitComplete;
 
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AtomReader.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AtomReader.java
similarity index 98%
rename from 
openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AtomReader.java
rename to 
openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AtomReader.java
index ca3e46e..580b1ab 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/AtomReader.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/AtomReader.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.service.quartz.scheduler;
+package org.apache.openmeetings.service.scheduler;
 
 import static 
org.apache.openmeetings.core.rss.LoadAtomRssFeed.getFeedConnection;
 
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/CleanupJob.java
similarity index 90%
rename from 
openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
rename to 
openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/CleanupJob.java
index 4ced109..5a8ba8e 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/CleanupJob.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.service.quartz.scheduler;
+package org.apache.openmeetings.service.scheduler;
 
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
 import static org.apache.openmeetings.util.OmFileHelper.TEST_SETUP_PREFIX;
@@ -33,12 +33,19 @@ import org.apache.openmeetings.db.entity.user.User;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
 
+@Component("cleanupJob")
 public class CleanupJob extends AbstractJob {
        private static Logger log = LoggerFactory.getLogger(CleanupJob.class);
+       @Value("${job.cleanup.session.timeout}")
        private long sessionTimeout = 30 * 60 * 1000L;
+       @Value("${job.cleanup.test.setup.timeout}")
        private long testSetupTimeout = 60 * 60 * 1000L; // 1 hour
+       @Value("${job.cleanup.reset.hash.ttl}")
        private long resetHashTtl = 24 * 60 * 60 * 1000L; // 1 day
+       @Value("${job.cleanup.conf.log.ttl}")
        private long confLogTtl = 7 * 24 * 60 * 60 * 1000L; // 7 days
 
        @Autowired
@@ -48,22 +55,6 @@ public class CleanupJob extends AbstractJob {
        @Autowired
        private ConferenceLogDao confLogDao;
 
-       public void setSessionTimeout(long sessionTimeout) {
-               this.sessionTimeout = sessionTimeout;
-       }
-
-       public void setTestSetupTimeout(long testSetupTimeout) {
-               this.testSetupTimeout = testSetupTimeout;
-       }
-
-       public void setResetHashTtl(long resetHashTtl) {
-               this.resetHashTtl = resetHashTtl;
-       }
-
-       public void setConfLogTtl(long confLogTtl) {
-               this.confLogTtl = confLogTtl;
-       }
-
        public void cleanTestSetup() {
                log.trace("CleanupJob.cleanTestSetup");
                final long now = System.currentTimeMillis();
diff --git 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/ReminderJob.java
 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/ReminderJob.java
similarity index 98%
rename from 
openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/ReminderJob.java
rename to 
openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/ReminderJob.java
index bd1e32a..9fadd6a 100644
--- 
a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/ReminderJob.java
+++ 
b/openmeetings-service/src/main/java/org/apache/openmeetings/service/scheduler/ReminderJob.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.service.quartz.scheduler;
+package org.apache.openmeetings.service.scheduler;
 
 import static org.apache.openmeetings.core.rss.LoadAtomRssFeed.setRss;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_RSS_FEED1;
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
index a57b704..f2bad46 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
@@ -24,11 +24,10 @@ import static 
org.apache.openmeetings.web.common.confirmation.ConfirmationBehavi
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.openmeetings.core.remote.KurentoHandler;
 import org.apache.openmeetings.core.remote.StreamProcessor;
@@ -76,14 +75,11 @@ public class ConnectionsPanel extends AdminBasePanel {
                        private static final long serialVersionUID = 1L;
 
                        private List<IDataProviderEntity> getConnections() {
-                               List<IDataProviderEntity> l = new ArrayList<>();
-                               l.addAll(cm.list());
-                               Collection<KStreamDto> streams = 
streamProcessor.getStreams()
+                               return Stream.concat(cm.stream()
+                                               , streamProcessor.getStreams()
                                                .stream()
                                                .map(KStreamDto::new)
-                                               .collect(Collectors.toList());
-                               l.addAll(streams);
-                               return l;
+                                       ).collect(Collectors.toList());
                        }
 
                        @Override
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
index 128f26f..aa73236 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
@@ -434,8 +434,7 @@ public class RoomForm extends AdminBaseForm<Room> {
 
        void updateClients(AjaxRequestTarget target) {
                long roomId = getModelObject().getId() != null ? 
getModelObject().getId() : 0;
-               final List<Client> clientsInRoom = cm.listByRoom(roomId);
-               clients.setDefaultModelObject(clientsInRoom);
+               
clients.setDefaultModelObject(cm.streamByRoom(roomId).collect(Collectors.toList()));
                target.add(clientsContainer);
        }
 
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
index ac4c389..13e0c97 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
@@ -39,10 +39,12 @@ import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 
 import javax.websocket.WebSocketContainer;
 
 import org.apache.openmeetings.IApplication;
+import org.apache.openmeetings.core.sip.SipManager;
 import org.apache.openmeetings.core.util.ChatWebSocketHelper;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
@@ -60,6 +62,7 @@ import org.apache.openmeetings.db.entity.room.RoomGroup;
 import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
+import org.apache.openmeetings.db.util.ApplicationHelper;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.apache.openmeetings.util.OmFileHelper;
@@ -185,6 +188,8 @@ public class Application extends 
AuthenticatedWebApplication implements IApplica
        private WhiteboardManager wbManager;
        @Autowired
        private AppointmentDao appointmentDao;
+       @Autowired
+       private SipManager sipManager;
 
        @Override
        protected void init() {
@@ -348,6 +353,11 @@ public class Application extends 
AuthenticatedWebApplication implements IApplica
                        recordingDao.resetProcessingStatus(); //we are starting 
so all processing recordings are now errors
                        userManager.initHttpClient();
                        setInitComplete(true);
+                       CompletableFuture.runAsync(() -> {
+                               ThreadContext.setApplication(Application.this);
+                               
ApplicationHelper.ensureRequestCycle(Application.this);
+                               sipManager.setUserPicture(u -> 
ProfileImageResourceReference.getUrl(RequestCycle.get(), u));
+                       });
                } catch (Exception err) {
                        log.error("[appStart]", err);
                }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
index 317f9ee..4dfedcc 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
@@ -23,21 +23,21 @@ import static 
org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.pages.auth.SignInPage.TOKEN_PARAM;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.openmeetings.core.remote.KurentoHandler;
+import org.apache.openmeetings.core.sip.SipManager;
 import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.log.ConferenceLog;
@@ -78,6 +78,10 @@ public class ClientManager implements IClientManager {
        private Application app;
        @Autowired
        private KurentoHandler kHandler;
+       @Autowired
+       private SipManager sipManager;
+       @Autowired
+       private TimerService timerService;
 
        private IMap<String, Client> map() {
                return app.hazelcast.getMap(ONLINE_USERS_KEY);
@@ -170,14 +174,12 @@ public class ClientManager implements IClientManager {
                if (roomId != null) {
                        IMap<Long, Set<String>> rooms = rooms();
                        rooms.lock(roomId);
-                       Set<String> clients = rooms.get(roomId);
-                       if (clients != null) {
-                               clients.remove(c.getUid());
-                               rooms.put(roomId, clients);
-                               onlineRooms.put(roomId, clients);
-                       }
+                       Set<String> clients = rooms.getOrDefault(roomId, 
ConcurrentHashMap.newKeySet());
+                       clients.remove(c.getUid());
+                       rooms.put(roomId, clients);
+                       onlineRooms.put(roomId, clients);
                        rooms.unlock(roomId);
-                       if (clients == null || clients.isEmpty()) {
+                       if (clients.isEmpty()) {
                                String serverId = c.getServerId();
                                IMap<String, ServerInfo> servers = servers();
                                servers.lock(serverId);
@@ -258,8 +260,7 @@ public class ClientManager implements IClientManager {
                log.debug("Adding online room client: {}, room: {}", 
c.getUid(), roomId);
                IMap<Long, Set<String>> rooms = rooms();
                rooms.lock(roomId);
-               rooms.putIfAbsent(roomId, ConcurrentHashMap.newKeySet());
-               Set<String> set = rooms.get(roomId);
+               Set<String> set = rooms.getOrDefault(roomId, 
ConcurrentHashMap.newKeySet());
                set.add(c.getUid());
                final int count = set.size();
                rooms.put(roomId, set);
@@ -268,6 +269,7 @@ public class ClientManager implements IClientManager {
                String serverId = c.getServerId();
                addRoomToServer(serverId, r);
                update(c);
+               timerService.scheduleSipCheck(r);
                return count;
        }
 
@@ -296,8 +298,8 @@ public class ClientManager implements IClientManager {
        }
 
        @Override
-       public List<Client> list() {
-               return new ArrayList<>(map().values());
+       public Stream<Client> stream() {
+               return map().values().stream();
        }
 
        @Override
@@ -306,50 +308,24 @@ public class ClientManager implements IClientManager {
        }
 
        @Override
-       public List<Client> listByRoom(Long roomId) {
-               return listByRoom(roomId, null);
-       }
-
-       public List<Client> listByRoom(Long roomId, Predicate<Client> filter) {
-               List<Client> clients = new ArrayList<>();
-               if (roomId != null) {
-                       Set<String> uids = onlineRooms.get(roomId);
-                       if (uids != null) {
-                               for (String uid : uids) {
-                                       Client c = get(uid);
-                                       if (c != null && (filter == null || 
filter.test(c))) {
-                                               clients.add(c);
-                                       }
-                               }
-                       }
-               }
-               return clients;
-       }
-
-       public Set<Long> listRoomIds(Long userId) {
-               Set<Long> result = new HashSet<>();
-               for (Entry<Long, Set<String>> me : onlineRooms.entrySet()) {
-                       for (String uid : me.getValue()) {
-                               Client c = get(uid);
-                               if (c != null && c.sameUserId(userId)) {
-                                       result.add(me.getKey());
-                               }
-                       }
-               }
-               return result;
+       public Stream<Client> streamByRoom(Long roomId) {
+               return Optional.ofNullable(roomId)
+                       .map(id -> onlineRooms.getOrDefault(id, Set.of()))
+                       .stream()
+                       .flatMap(Set::stream)
+                       .map(uid -> get(uid))
+                       .filter(Objects::nonNull);
        }
 
        public boolean isInRoom(long roomId, long userId) {
-               Set<String> clients = onlineRooms.get(roomId);
-               if (clients != null) {
-                       for (String uid : clients) {
-                               Client c = get(uid);
-                               if (c != null && c.sameUserId(userId)) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
+               return Optional.of(roomId)
+                       .map(id -> onlineRooms.getOrDefault(id, Set.of()))
+                       .stream()
+                       .flatMap(Set::stream)
+                       .map(uid -> get(uid))
+                       .filter(c -> c != null && c.sameUserId(userId))
+                       .findAny()
+                       .isPresent();
        }
 
        private List<Client> getByKeys(Long userId, String sessionId) {
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/TimerService.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/TimerService.java
index 906426f..799b5a1 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/TimerService.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/TimerService.java
@@ -21,13 +21,17 @@ package org.apache.openmeetings.web.app;
 import static java.util.concurrent.CompletableFuture.delayedExecutor;
 
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 
 import javax.annotation.PostConstruct;
 
+import org.apache.openmeetings.core.sip.SipManager;
 import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.user.User;
@@ -42,15 +46,19 @@ import org.springframework.stereotype.Service;
 public class TimerService {
        private static final Logger log = 
LoggerFactory.getLogger(TimerService.class);
        private int modCheckInterval = 5;
+       private int sipCheckInterval = 2;
        private final User sysUser = new User();
        private final Map<Long, CompletableFuture<Object>> modCheckMap = new 
ConcurrentHashMap<>();
+       private final Map<Long, CompletableFuture<Object>> sipCheckMap = new 
ConcurrentHashMap<>();
 
        @Autowired
        private ClientManager cm;
+       @Autowired
+       private SipManager sipManager;
 
        @PostConstruct
        private void init() {
-               sysUser.setId(-1L);
+               sysUser.setId(-5L);
                sysUser.setDisplayName("System");
        }
 
@@ -59,19 +67,64 @@ public class TimerService {
                                roomId
                                , new CompletableFuture<>().completeAsync(() -> 
{
                                        log.warn("Moderator room check {}", 
roomId);
-                                       if (cm.listByRoom(roomId).isEmpty()) {
+                                       if 
(cm.streamByRoom(roomId).findAny().isEmpty()) {
                                                modCheckMap.remove(roomId);
                                        } else {
-                                               WebSocketHelper.sendRoom(new 
TextRoomMessage(roomId, sysUser, RoomMessage.Type.MODERATOR_IN_ROOM, "" + 
!cm.listByRoom(roomId, c -> c.hasRight(Right.MODERATOR)).isEmpty()));
+                                               WebSocketHelper.sendRoom(new 
TextRoomMessage(roomId, sysUser, RoomMessage.Type.MODERATOR_IN_ROOM
+                                                               , "" + 
!cm.streamByRoom(roomId).filter(c -> 
c.hasRight(Right.MODERATOR)).findAny().isEmpty()));
                                                doModCheck(roomId);
                                        }
                                        return null;
                                }, delayedExecutor(modCheckInterval, 
TimeUnit.SECONDS)));
        }
 
+       private void doSipCheck(Long roomId) {
+               sipCheckMap.put(
+                               roomId
+                               , new CompletableFuture<>().completeAsync(() -> 
{
+                                       log.warn("Sip room check {}", roomId);
+                                       Optional<Client> sipClient = 
cm.streamByRoom(roomId).filter(Client::isSip).findAny();
+                                       
cm.streamByRoom(roomId).filter(Predicate.not(Client::isSip)).findAny().ifPresentOrElse(c
 -> {
+                                               updateSipLastName(sipClient, 
c.getRoom());
+                                               doSipCheck(roomId);
+                                       }, () -> {
+                                               log.warn("No more clients in 
the room {}", roomId);
+                                               sipCheckMap.remove(roomId);
+                                               sipClient.ifPresent(cm::exit);
+                                       });
+                                       return null;
+                               }, delayedExecutor(sipCheckInterval, 
TimeUnit.SECONDS)));
+       }
+
+       private void updateSipLastName(Optional<Client> sipClient, Room r) {
+               final String newLastName = "(" + 
sipManager.countUsers(r.getConfno()) + ")";
+               sipClient.ifPresentOrElse(c -> {
+                       if (!newLastName.equals(c.getUser().getLastname())) {
+                               
c.getUser().setLastname(newLastName).resetDisplayName();
+                               cm.update(c);
+                               WebSocketHelper.sendRoom(new 
TextRoomMessage(r.getId(), c, RoomMessage.Type.RIGHT_UPDATED, c.getUid()));
+                       }
+               }, () -> {
+                       User sipUser = sipManager.getSipUser(r);
+                       sipUser.setLastname(newLastName).resetDisplayName();
+                       Client c = new Client("-- unique - sip - session --", 
1, sipUser, sipUser.getPictureUri());
+                       cm.add(c);
+                       c.setRoom(r);
+                       cm.addToRoom(c);
+                       WebSocketHelper.sendRoom(new TextRoomMessage(r.getId(), 
c, RoomMessage.Type.ROOM_ENTER, c.getUid()));
+               });
+       }
+
        public void scheduleModCheck(Room r) {
-               if (r.isModerated() && r.isWaitModerator() && 
!cm.listByRoom(r.getId()).isEmpty() && !modCheckMap.containsKey(r.getId())) {
+               if (r.isModerated() && r.isWaitModerator() && 
!modCheckMap.containsKey(r.getId())) {
                        doModCheck(r.getId());
                }
        }
+
+       public void scheduleSipCheck(Room r) {
+               // sip allowed and configured
+               if (sipManager.getSipUser(r) != null && 
!sipCheckMap.containsKey(r.getId())) {
+                       doSipCheck(r.getId());
+               }
+       }
 }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
index 53265d5..76a8dcf 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
@@ -223,9 +223,7 @@ public class UserManager implements IUserManager {
        @Override
        public boolean kickUsersByRoomId(Long roomId) {
                try {
-                       for (Client c : cm.listByRoom(roomId)) {
-                               Application.kickUser(c);
-                       }
+                       cm.streamByRoom(roomId).forEach(Application::kickUser);
                        return true;
                } catch (Exception err) {
                        log.error("[kickUsersByRoomId]", err);
@@ -235,7 +233,7 @@ public class UserManager implements IUserManager {
 
        @Override
        public boolean kickExternal(Long roomId, String externalType, String 
externalId) {
-               boolean result = false;
+               Boolean result = false;
                try {
                        if (roomId == null) {
                                return result;
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
index 73f9049..4a4ae17 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
@@ -44,7 +44,6 @@ import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
 import org.apache.openmeetings.db.dao.file.FileItemDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.Client.StreamDesc;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
 import org.apache.openmeetings.db.entity.calendar.MeetingMember;
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
@@ -167,9 +166,9 @@ public class RoomPanel extends BasePanel {
                                sidebar.setFilesActive(target);
                        }
                        if (Room.Type.PRESENTATION != r.getType()) {
-                               List<Client> mods = cm.listByRoom(r.getId(), cl 
-> cl.hasRight(Room.Right.MODERATOR));
-                               log.debug("RoomPanel::roomEnter, mods IS EMPTY 
? {}, is MOD ? {}", mods.isEmpty(), c.hasRight(Room.Right.MODERATOR));
-                               if (mods.isEmpty()) {
+                               boolean modsEmpty = noModerators();
+                               log.debug("RoomPanel::roomEnter, mods IS EMPTY 
? {}, is MOD ? {}", modsEmpty, c.hasRight(Room.Right.MODERATOR));
+                               if (modsEmpty) {
                                        showIdeaAlert(target, 
getString(r.isModerated() ? "641" : "498"));
                                }
                        }
@@ -182,11 +181,10 @@ public class RoomPanel extends BasePanel {
                private void initVideos(AjaxRequestTarget target) {
                        StringBuilder sb = new StringBuilder();
                        JSONArray streams = new JSONArray();
-                       for (Client c : cm.listByRoom(getRoom().getId())) {
-                               for (StreamDesc sd : c.getStreams()) {
-                                       streams.put(sd.toJson());
-                               }
-                       }
+                       cm.streamByRoom(getRoom().getId())
+                               .map(Client::getStreams)
+                               .flatMap(List::stream)
+                               .forEach(sd -> streams.put(sd.toJson()));
                        if (streams.length() > 0) {
                                
sb.append("VideoManager.play(").append(streams).append(", 
").append(kHandler.getTurnServers(getClient())).append(");");
                        }
@@ -312,7 +310,7 @@ public class RoomPanel extends BasePanel {
                add(roomClosed = new RedirectMessageDialog("room-closed", 
"1098", r.isClosed(), r.getRedirectURL()));
                if (r.isClosed()) {
                        room.setVisible(false);
-               } else if (cm.listByRoom(r.getId()).size() >= r.getCapacity()) {
+               } else if (cm.streamByRoom(r.getId()).count() >= 
r.getCapacity()) {
                        accessDenied = new 
ExpiredMessageDialog(ACCESS_DENIED_ID, getString("99"), menu);
                        room.setVisible(false);
                } else if (r.getId().equals(WebSession.get().getRoomId())) {
@@ -397,7 +395,7 @@ public class RoomPanel extends BasePanel {
                        }
                        if (r.isModerated() && r.isWaitModerator()
                                        && !c.hasRight(Right.MODERATOR)
-                                       && cm.listByRoom(r.getId(), cl -> 
cl.hasRight(Right.MODERATOR)).isEmpty())
+                                       && noModerators())
                        {
                                room.setVisible(false);
                                createWaitModerator(true);
@@ -620,13 +618,9 @@ public class RoomPanel extends BasePanel {
                        if (streamProcessor.isRecording(r.getId())) {
                                handler.appendJavaScript("if (typeof(WbArea) 
=== 'object') {WbArea.setRecStarted(true);}");
                        } else if 
(streamProcessor.recordingAllowed(getClient())) {
-                               boolean hasStreams = false;
-                               for (Client cl : cm.listByRoom(r.getId())) {
-                                       if (!cl.getStreams().isEmpty()) {
-                                               hasStreams = true;
-                                               break;
-                                       }
-                               }
+                               boolean hasStreams = cm.streamByRoom(r.getId())
+                                               .filter(cl -> 
!cl.getStreams().isEmpty())
+                                               .findAny().isPresent();
                                handler.appendJavaScript(String.format("if 
(typeof(WbArea) === 'object') 
{WbArea.setRecStarted(false);WbArea.setRecEnabled(%s);}", hasStreams));
                        }
                }
@@ -641,12 +635,9 @@ public class RoomPanel extends BasePanel {
        }
 
        public static boolean hasRight(ClientManager cm, long userId, long 
roomId, Right r) {
-               for (Client c : cm.listByRoom(roomId)) {
-                       if (c.sameUserId(userId) && c.hasRight(r)) {
-                               return true;
-                       }
-               }
-               return false;
+               return cm.streamByRoom(roomId)
+                               .filter(c -> c.sameUserId(userId) && 
c.hasRight(r))
+                               .findAny().isPresent();
        }
 
        @Override
@@ -699,8 +690,7 @@ public class RoomPanel extends BasePanel {
 
        public void requestRight(Right right, IPartialPageRequestHandler 
handler) {
                RoomMessage.Type reqType = null;
-               List<Client> mods = cm.listByRoom(r.getId(), c -> 
c.hasRight(Room.Right.MODERATOR));
-               if (mods.isEmpty()) {
+               if (noModerators()) {
                        if (r.isModerated()) {
                                showIdeaAlert(handler, getString("696"));
                                return;
@@ -881,7 +871,7 @@ public class RoomPanel extends BasePanel {
 
        private CharSequence createAddClientJs(Client c) {
                JSONArray arr = new JSONArray();
-               cm.listByRoom(r.getId()).stream().forEach(cl -> 
arr.put(cl.toJson(c.getUid().equals(cl.getUid()))));
+               cm.streamByRoom(r.getId()).forEach(cl -> 
arr.put(cl.toJson(c.getUid().equals(cl.getUid()))));
                return new StringBuilder()
                                .append("Room.addClient(")
                                .append(arr.toString(new NullStringer()))
@@ -909,4 +899,11 @@ public class RoomPanel extends BasePanel {
                }
                return res;
        }
+
+       private boolean noModerators() {
+               return cm.streamByRoom(r.getId())
+                               .filter(cl -> cl.hasRight(Room.Right.MODERATOR))
+                               .findAny()
+                               .isEmpty();
+       }
 }
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/SipDialerDialog.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/SipDialerDialog.java
index eb31b18..e54a13a 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/SipDialerDialog.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/SipDialerDialog.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.room.menu;
 
-import org.apache.openmeetings.db.dao.room.SipDao;
+import org.apache.openmeetings.core.sip.SipManager;
 import org.apache.openmeetings.web.common.OmModalCloseButton;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -41,7 +41,7 @@ public class SipDialerDialog extends Modal<String> {
        private final TextField<String> number = new TextField<>("number", 
Model.of(""));
        private final RoomPanel room;
        @SpringBean
-       private SipDao sipDao;
+       private SipManager sipDao;
 
        public SipDialerDialog(String id, RoomPanel room) {
                super(id);
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
index c7b8232..df58970 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.room.wb;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.alwaysTrue;
 import static org.apache.openmeetings.core.util.WebSocketHelper.publish;
 import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_SRC;
@@ -74,7 +75,7 @@ public class WbWebSocketHelper {
                if (publish) {
                        publish(new WsMessageWb(roomId, meth, obj, null));
                }
-               sendWb(roomId, meth, obj, null);
+               sendWb(roomId, meth, obj, alwaysTrue());
        }
 
        public static void sendWbOthers(Long roomId, WbAction meth, JSONObject 
obj, final String uid) {
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
index 9aca029..9791449 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
@@ -38,7 +38,6 @@ import java.util.List;
 import org.apache.openmeetings.core.mail.MailHandler;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
-import org.apache.openmeetings.db.dao.room.IInvitationManager;
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dao.user.PrivateMessageDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -50,6 +49,7 @@ import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.PrivateMessage;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
+import org.apache.openmeetings.db.manager.IInvitationManager;
 import org.apache.openmeetings.util.CalendarHelper;
 import org.apache.openmeetings.web.app.Application;
 import org.apache.openmeetings.web.app.WebSession;
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
index cd4dfd4..3576fc8 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
@@ -70,7 +70,7 @@ public class RoomListPanel extends Panel {
                        final WebMarkupContainer info = new 
WebMarkupContainer("info");
                        roomContainer.add(info.setOutputMarkupId(true)
                                        
.add(AttributeModifier.append(ATTR_TITLE, 
getString(String.format("room.type.%s.desc", r.getType().name())))));
-                       final Label curUsers = new Label("curUsers", new 
Model<>(cm.listByRoom(r.getId()).size()));
+                       final Label curUsers = new Label("curUsers", new 
Model<>(cm.streamByRoom(r.getId()).count()));
                        roomContainer.add(curUsers.setOutputMarkupId(true));
                        roomContainer.add(new Label("totalUsers", 
r.getCapacity()));
                        item.add(new WebMarkupContainer("btn").add(new 
Label("label", label)).add(new RoomEnterBehavior(r.getId()) {
@@ -95,7 +95,7 @@ public class RoomListPanel extends Panel {
 
                                @Override
                                public void onClick(AjaxRequestTarget target) {
-                                       
target.add(curUsers.setDefaultModelObject(cm.listByRoom(r.getId()).size()));
+                                       
target.add(curUsers.setDefaultModelObject(cm.streamByRoom(r.getId()).count()));
                                        onRefreshClick(target, r);
                                }
                        }.add(AttributeModifier.append(ATTR_TITLE, new 
ResourceModel("lbl.refresh"))));
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
index f110da7..4760969 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
@@ -22,6 +22,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -128,7 +129,7 @@ public class RoomsPanel extends UserPanel {
        }
 
        void updateRoomDetails(AjaxRequestTarget target) {
-               clients.setDefaultModelObject(cm.listByRoom(roomId));
+               
clients.setDefaultModelObject(cm.streamByRoom(roomId).collect(Collectors.toList()));
                Room room = roomDao.get(roomId);
                roomIdLbl.setDefaultModelObject(room.getId());
                roomNameLbl.setDefaultModelObject(room.getName());
diff --git 
a/openmeetings-web/src/main/webapp/WEB-INF/classes/applicationContext.xml 
b/openmeetings-web/src/main/webapp/WEB-INF/classes/applicationContext.xml
index 0edbe2b..e8391ad 100644
--- a/openmeetings-web/src/main/webapp/WEB-INF/classes/applicationContext.xml
+++ b/openmeetings-web/src/main/webapp/WEB-INF/classes/applicationContext.xml
@@ -40,18 +40,9 @@
        <context:annotation-config />
        <context:component-scan base-package="org.apache.openmeetings" />
 
-       <!--
-                       5000            == 5 sec
-                       300000          == 5 min
-                       900000          == 15 min
-                       1800000         == 30 min
-                       3600000         == 1 hour
-                       86400000        == 1 day
-                       604800000       == 7 days
-        -->
-       <bean id="cleanupJob" 
class="org.apache.openmeetings.service.quartz.scheduler.CleanupJob"
-                       p:sessionTimeout="1800000" p:testSetupTimeout="3600000" 
p:resetHashTtl="86400000"
-                       p:confLogTtl="604800000" />
+       <bean 
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
+               p:location="classpath:openmeetings.properties" />
+
        <!-- sessions clean-up -->
        <bean id="cleanSessionsJobDetails" 
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
                        p:targetObject-ref="cleanupJob" 
p:targetMethod="cleanSessions" p:concurrent="false" />
@@ -124,46 +115,10 @@
        </bean>
        <!-- End of Services -->
 
-       <bean class="org.apache.openmeetings.db.dao.room.SipConfig">
-               <!-- Should be uncommented and updated with real values for 
Asterisk-
-               <property name="sipHostname" value="192.168.1.102"/>
-               <property name="managerPort" value="5038"/>
-               <property name="managerUser" value="openmeetings"/>
-               <property name="managerPass" value="12345"/>
-               <property name="managerTimeout" value="10000"/>
-
-               <property name="localWsPort" value="6666"/>
-               <property name="localWsHost" value="192.168.1.211"/>
-               <property name="wsPort" value="8088"/>
-               <property name="omSipUser" value="omsip_user"/>
-               <property name="omSipPasswd" value="12345"/>
-
-               <property name="uid" 
value="87dddad4-9ca5-475b-860f-2e0825d02b76"/>
-               -  -->
-       </bean>
-
        <!-- Thread Executor -->
        <bean id="taskExecutor" 
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
                <property name="corePoolSize" value="5" />
                <property name="maxPoolSize" value="10" />
                <property name="queueCapacity" value="25" />
        </bean>
-
-       <!-- Kurento -->
-       <!-- please ensure `p:kuid` below is unique, better to regenerate it 
from time to time -->
-       <!-- `p:ignoredKuids` can be space and/or comma separated -->
-       <bean id="kurentoHandler" 
class="org.apache.openmeetings.core.remote.KurentoHandler"
-                       p:kurentoWsUrl="ws://127.0.0.1:8888/kurento"
-                       p:checkTimeout="10000"
-                       p:watchThreadCount="10"
-                       p:turnUrl=""
-                       p:turnUser=""
-                       p:turnSecret=""
-                       p:turnMode="rest"
-                       p:turnTtl="60"
-                       p:objCheckTimeout="200"
-                       p:flowoutTimeout="5"
-                       p:kuid="df992960-e7b0-11ea-9acd-337fb30dd93d"
-                       p:ignoredKuids=""
-                       />
 </beans>
diff --git 
a/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties 
b/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties
new file mode 100644
index 0000000..5f566d8
--- /dev/null
+++ b/openmeetings-web/src/main/webapp/WEB-INF/classes/openmeetings.properties
@@ -0,0 +1,65 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+################## Timeouts ##################
+#                      5000            == 5 sec
+#                      300000          == 5 min
+#                      900000          == 15 min
+#                      1800000         == 30 min
+#                      3600000         == 1 hour
+#                      86400000        == 1 day
+#                      604800000       == 7 days
+job.cleanup.session.timeout=1800000
+job.cleanup.test.setup.timeout=3600000
+job.cleanup.reset.hash.ttl=86400000
+job.cleanup.conf.log.ttl=604800000
+
+################## Kurento ##################
+kurento.ws.url=ws://127.0.0.1:8888/kurento
+kurento.turn.url=
+kurento.turn.user=
+kurento.turn.secret=
+kurento.turn.mode=rest
+## minutes
+kurento.turn.ttl=60
+## milliseconds
+kurento.check.timeout=10000
+## milliseconds
+kurento.object.check.timeout=200
+kurento.watch.thread.count=10
+kurento.flowout.timeout=5
+## please ensure this one is unique, better to regenerate it from time to time
+## can be generated for ex. here https://www.uuidtools.com
+kurento.kuid=df992960-e7b0-11ea-9acd-337fb30dd93d
+## this list can be space and/or comma separated
+kurento.ignored.kuids=
+
+################## SIP ##################
+### Should be uncommented and updated with real values for Asterisk ###
+#sip.hostname=192.168.1.102
+#sip.manager.port=5038
+#sip.manager.user=openmeetings
+#sip.manager.password=12345
+#sip.manager.timeout=10000
+
+#sip.ws.local.port=6666
+## 127.0.0.1 is NOT working here
+#sip.ws.local.host=192.168.1.211
+#sip.ws.remote.port=8088
+#sip.ws.remote.user=omsip_user
+#sip.ws.remote.password=12345
diff --git 
a/openmeetings-web/src/test/java/org/apache/openmeetings/service/quartz/TestJob.java
 
b/openmeetings-web/src/test/java/org/apache/openmeetings/service/quartz/TestJob.java
index 01be361..58edf05 100644
--- 
a/openmeetings-web/src/test/java/org/apache/openmeetings/service/quartz/TestJob.java
+++ 
b/openmeetings-web/src/test/java/org/apache/openmeetings/service/quartz/TestJob.java
@@ -23,8 +23,8 @@ import static 
org.apache.openmeetings.util.OpenmeetingsVariables.setInitComplete
 
 import org.apache.openmeetings.AbstractWicketTester;
 import org.apache.openmeetings.db.entity.basic.Configuration;
-import org.apache.openmeetings.service.quartz.scheduler.CleanupJob;
-import org.apache.openmeetings.service.quartz.scheduler.ReminderJob;
+import org.apache.openmeetings.service.scheduler.CleanupJob;
+import org.apache.openmeetings.service.scheduler.ReminderJob;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
diff --git 
a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
 
b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
index dd4a83c..d704577 100644
--- 
a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
+++ 
b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
@@ -22,6 +22,7 @@ import static 
org.apache.openmeetings.webservice.Constants.TNS;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.jws.WebMethod;
 import javax.jws.WebParam;
@@ -46,7 +47,6 @@ import 
org.apache.openmeetings.db.dto.basic.ServiceResult.Type;
 import org.apache.openmeetings.db.dto.room.InvitationDTO;
 import org.apache.openmeetings.db.dto.room.RoomDTO;
 import org.apache.openmeetings.db.dto.user.UserDTO;
-import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Invitation;
 import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
 import org.apache.openmeetings.db.entity.room.Room;
@@ -349,7 +349,7 @@ public class RoomWebService extends BaseWebService {
        @GET
        @Path("/count/{roomid}")
        public ServiceResult count(@WebParam(name="sid") @QueryParam("sid") 
String sid, @WebParam(name="roomid") @PathParam("roomid") Long roomId) {
-               return performCall(sid, User.Right.SOAP, sd -> new 
ServiceResult(String.valueOf(clientManager.listByRoom(roomId).size()), 
Type.SUCCESS));
+               return performCall(sid, User.Right.SOAP, sd -> new 
ServiceResult(String.valueOf(clientManager.streamByRoom(roomId).count()), 
Type.SUCCESS));
        }
 
        /**
@@ -364,11 +364,9 @@ public class RoomWebService extends BaseWebService {
        @Path("/users/{roomid}")
        public List<UserDTO> users(@WebParam(name="sid") @QueryParam("sid") 
String sid, @WebParam(name="roomid") @PathParam("roomid") Long roomId) {
                return performCall(sid, User.Right.SOAP, sd -> {
-                       List<UserDTO> result = new ArrayList<>();
-                       for (Client c : clientManager.listByRoom(roomId)) {
-                               result.add(new UserDTO(c.getUser()));
-                       }
-                       return result;
+                       return clientManager.streamByRoom(roomId)
+                                       .map(c -> new UserDTO(c.getUser()))
+                                       .collect(Collectors.toList());
                });
        }
 
diff --git a/pom.xml b/pom.xml
index 60b4cf4..959ee7e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -616,18 +616,6 @@
                                </exclusions>
                        </dependency>
                        <dependency>
-                               <groupId>org.mockito</groupId>
-                               <artifactId>mockito-inline</artifactId>
-                               <version>${mockito.version}</version>
-                               <scope>test</scope>
-                       </dependency>
-                       <dependency>
-                               <groupId>org.mockito</groupId>
-                               <artifactId>mockito-junit-jupiter</artifactId>
-                               <version>${mockito.version}</version>
-                               <scope>test</scope>
-                       </dependency>
-                       <dependency>
                                <groupId>javax.websocket</groupId>
                                <artifactId>javax.websocket-api</artifactId>
                                <version>1.1</version>
@@ -644,16 +632,33 @@
                                <version>4.3.0</version>
                        </dependency>
                        <dependency>
-                               <groupId>org.junit.jupiter</groupId>
-                               <artifactId>junit-jupiter-params</artifactId>
-                               <version>${junit.version}</version>
-                               <scope>test</scope>
+                               <groupId>org.asteriskjava</groupId>
+                               <artifactId>asterisk-java</artifactId>
+                               <version>${asterisk-java.version}</version>
                        </dependency>
                        <dependency>
                                <groupId>javax.sip</groupId>
                                <artifactId>jain-sip-ri</artifactId>
                                <version>${jain-sip.version}</version>
                        </dependency>
+                       <dependency>
+                               <groupId>org.mockito</groupId>
+                               <artifactId>mockito-inline</artifactId>
+                               <version>${mockito.version}</version>
+                               <scope>test</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.mockito</groupId>
+                               <artifactId>mockito-junit-jupiter</artifactId>
+                               <version>${mockito.version}</version>
+                               <scope>test</scope>
+                       </dependency>
+                       <dependency>
+                               <groupId>org.junit.jupiter</groupId>
+                               <artifactId>junit-jupiter-params</artifactId>
+                               <version>${junit.version}</version>
+                               <scope>test</scope>
+                       </dependency>
                </dependencies>
        </dependencyManagement>
        <dependencies>

Reply via email to