diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java
index d5562595647c8e667561507bed7284bc40f206ea..ce1c25bb2329733c64c0dad0034ac1260f29e624 100755
--- a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java
+++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java
@@ -79,6 +79,9 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
 		for (IConnection conn : conns) {
 			String connUserId = (String) conn.getAttribute("USERID");
 			String connSessionId = conn.getSessionId();
+			String clientId = conn.getClient().getId();
+			String remoteHost = conn.getRemoteAddress();
+			int remotePort = conn.getRemotePort();
 			if (connUserId != null && connUserId.equals(userId) && !connSessionId.equals(sessionId)) {
 				conn.removeAttribute("USERID");
 				Map<String, Object> logData = new HashMap<String, Object>();
@@ -86,6 +89,8 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
 				logData.put("userId", userId);
 				logData.put("oldConnId", connSessionId);
 				logData.put("newConnId", sessionId);
+				logData.put("clientId", clientId);
+				logData.put("remoteAddress", remoteHost + ":" + remotePort);
 				logData.put("event", "removing_defunct_connection");
 				logData.put("description", "Removing defunct connection BBB Video.");
 
@@ -96,11 +101,17 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
 			  }
 		  }
 
+	  String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
+	  int remotePort = Red5.getConnectionLocal().getRemotePort();
+	  String clientId = Red5.getConnectionLocal().getClient().getId();
+
 		Map<String, Object> logData = new HashMap<String, Object>();
 		logData.put("meetingId", meetingId);
 		logData.put("userId", userId);
 		logData.put("connType", connType);
 		logData.put("connId", sessionId);
+	  logData.put("clientId", clientId);
+	  logData.put("remoteAddress", remoteHost + ":" + remotePort);
 		logData.put("event", "user_joining_bbb_video");
 		logData.put("description", "User joining BBB Video.");
 
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java
index ee600aab123798fba46d0dfd44e3963eba47a1ec..d508fa1f8f88316f7d5977935295fffc5e1d1673 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java
@@ -138,6 +138,9 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
 		for (IConnection conn : conns) {
 			String connUserId = (String) conn.getAttribute("INTERNAL_USER_ID");
 			String connSessionId = conn.getSessionId();
+			String clientId = conn.getClient().getId();
+			String remoteHost = connection.getRemoteAddress();
+			int remotePort = connection.getRemotePort();
 			if (connUserId != null && connUserId.equals(userId) && !connSessionId.equals(sessionId)) {
 				conn.removeAttribute("INTERNAL_USER_ID");
 				Map<String, Object> logData = new HashMap<String, Object>();
@@ -145,6 +148,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
 				logData.put("userId", userId);
 				logData.put("oldConnId", connSessionId);
 				logData.put("newConnId", sessionId);
+				logData.put("clientId", clientId);
+				logData.put("remoteAddress", remoteHost + ":" + remotePort);
 				logData.put("event", "removing_defunct_connection");
 				logData.put("description", "Removing defunct connection BBB Apps.");
 
@@ -173,14 +178,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
 	    String connId = Red5.getConnectionLocal().getSessionId();	        
 		
 		String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
-		int remotePort = Red5.getConnectionLocal().getRemotePort();   
-
+		int remotePort = Red5.getConnectionLocal().getRemotePort();
+		String clientId = Red5.getConnectionLocal().getClient().getId();
 
 		Map<String, Object> logData = new HashMap<String, Object>();
 		logData.put("meetingId", meetingId);
 		logData.put("connType", connType);
 		logData.put("connId", connId);
-		logData.put("conn", remoteHost + ":" + remotePort);
+		logData.put("clientId", clientId);
+		logData.put("remoteAddress", remoteHost + ":" + remotePort);
 		logData.put("userId", userId);
 		logData.put("externalUserId", externalUserID);
 		logData.put("sessionId", sessionId);
@@ -222,14 +228,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
 	    String connType = getConnectionType(Red5.getConnectionLocal().getType());
 	    String userFullname = bbbSession.getUsername();
 	    String connId = Red5.getConnectionLocal().getSessionId();
-	    
+		String clientId = Red5.getConnectionLocal().getClient().getId();
         String sessionId =  CONN + userId;
 	    	    
 	    Map<String, Object> logData = new HashMap<String, Object>();
 	    logData.put("meetingId", meetingId);
 	    logData.put("connType", connType);
 	    logData.put("connId", connId);
-	    logData.put("conn", remoteHost + ":" + remotePort);
+		logData.put("clientId", clientId);
+		logData.put("remoteAddress", remoteHost + ":" + remotePort);
 	    logData.put("sessionId", sessionId);
 	    logData.put("userId", userId);
 	    logData.put("username", userFullname);
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
index 0ce096841767dd6d51ebc08023cdf18277e9f904..2d23b2118c7bbf49c41e6262b7571179ec8610e0 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java
@@ -145,8 +145,8 @@ public class ConnectionInvokerService {
   private void handlDisconnectClientMessage(DisconnectClientMessage msg) {
     IScope meetingScope = getScope(msg.getMeetingId());
     if (meetingScope != null) {
-      String sessionId = CONN + msg.getUserId();
-      IConnection conn = getConnection(meetingScope, sessionId);
+      String userId = msg.getUserId();
+      IConnection conn = getConnection(meetingScope, userId);
       if (conn != null) {
         if (conn.isConnected()) {
           log.info("Disconnecting user=[{}] from meeting=[{}]", msg.getUserId(), msg.getMeetingId());
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as
index 1c4f27e3a64fd24ad8cc41d782fa9c997af2453d..0ab10e7dd69c7bb9a315d0c64506df1de94039fd 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as
@@ -144,7 +144,7 @@ package org.bigbluebutton.main.model
 			try {
                 var logData:Object = UsersUtil.initLogData();
                 logData.connection = this.baseURI;
-                logData.tags = ["connection"];
+                logData.tags = ["initialization", "port-test", "connection"];
                 logData.message = "Port testing connection.";
                 LOGGER.info(JSON.stringify(logData));
         
@@ -168,7 +168,7 @@ package org.bigbluebutton.main.model
 		public function connectionTimeout (e:TimerEvent) : void {
             var logData:Object = UsersUtil.initLogData();
             logData.connection = this.baseURI;
-            logData.tags = ["connection"];
+            logData.tags = ["initialization", "port-test", "connection"];
             logData.message = "Port testing connection timedout.";
             LOGGER.info(JSON.stringify(logData));
 
@@ -200,7 +200,7 @@ package org.bigbluebutton.main.model
     private function closeConnectionTimerHandler (e:TimerEvent) : void {
         var logData:Object = UsersUtil.initLogData();
         logData.connection = this.baseURI;
-        logData.tags = ["connection"];
+        logData.tags = ["initialization", "port-test", "connection"];
         logData.message = "Closing port testing connection.";
         LOGGER.info(JSON.stringify(logData));
 
@@ -222,7 +222,7 @@ package org.bigbluebutton.main.model
             
         var logData:Object = UsersUtil.initLogData();
         logData.connection = this.baseURI;
-        logData.tags = ["connection"];
+        logData.tags = ["initialization", "port-test", "connection"];
 
         
         if ( statusCode == "NetConnection.Connect.Success" ) {
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as
index a5cac6a9fd97a0b856f979517fbf51c55f2f45c7..ce92a8f875ecc6cbc3694b8dc4088c27cf00356c 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as
@@ -70,15 +70,18 @@ package org.bigbluebutton.main.model.modules
         var dispatcher:Dispatcher = new Dispatcher();
         dispatcher.dispatchEvent(new MeetingNotFoundEvent(result.response.logoutURL));			
       } else if (returncode == 'SUCCESS') {
-        logData.tags = ["initialization"];
-        logData.message = "Enter API call succeeded.";
-        LOGGER.info(JSON.stringify(logData));
+
           
         var response:Object = new Object();
         response.username = result.response.fullname;
         response.userId = result.response.internalUserID;
         response.meetingName = result.response.confname;
         response.meetingId = result.response.meetingID;
+        
+        logData.response = response;
+        logData.tags = ["initialization"];
+        logData.message = "Enter API call succeeded.";
+        LOGGER.info(JSON.stringify(logData));
          
         if (_resultListener != null) _resultListener(true, response);
       }
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
index 7b5cad45d88fa94b13458a3bf211cfa1104c1c06..ba6043e0d05df3c7c619d928bed8829031f91b86 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as
@@ -26,7 +26,7 @@ package org.bigbluebutton.main.model.users
 	import flash.events.TimerEvent;
 	import flash.net.NetConnection;
 	import flash.net.Responder;
-	import flash.utils.Timer;	
+	import flash.utils.Timer;
 	import org.as3commons.logging.api.ILogger;
 	import org.as3commons.logging.api.getClassLogger;
 	import org.bigbluebutton.core.BBB;
@@ -40,358 +40,397 @@ package org.bigbluebutton.main.model.users
 	import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent;
 	import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
   
-	public class NetConnectionDelegate
-	{
-		private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate);
-		
-		private var _netConnection:NetConnection;	
-		private var connectionId:Number;
-		private var connected:Boolean = false;
-		
-		private var _userid:Number = -1;
-		private var _role:String = "unknown";
-		
-		private var logoutOnUserCommand:Boolean = false;
-		private var dispatcher:Dispatcher;    
-    private var _messageListeners:Array = new Array();
-    
-    private var authenticated: Boolean = false;
-    private var reconnecting:Boolean = false;
-	private var numNetworkChangeCount:int = 0;
-	
-		private var _validateTokenTimer:Timer = null;	
-	
-		public function NetConnectionDelegate():void {
-			dispatcher = new Dispatcher();
-		}
-		  
-		public function get connection():NetConnection {
-			return _netConnection;
-		}
+    public class NetConnectionDelegate {
+        private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate);
+
+        private var _netConnection:NetConnection;	
+        private var connectionId:Number;
+        private var connected:Boolean = false;
+        private var _userid:Number = -1;
+        private var _role:String = "unknown";
+        private var logoutOnUserCommand:Boolean = false;
+        private var dispatcher:Dispatcher;    
+        private var _messageListeners:Array = new Array();
+        private var authenticated: Boolean = false;
+        private var reconnecting:Boolean = false;
         
-    public function addMessageListener(listener:IMessageListener):void {
-      _messageListeners.push(listener);
-    }
+        private var maxConnectAttempt:int = 2;
+        private var connectAttemptCount:int = 0;
+        private var connectAttemptTimeout:Number = 5000;
+        private var connectionTimer:Timer;
+    
+        private var numNetworkChangeCount:int = 0;
+        private var _validateTokenTimer:Timer = null;
+
+        private var bbbAppsUrl: String = null;
         
-    public function removeMessageListener(listener:IMessageListener):void {
-      for (var ob:int=0; ob<_messageListeners.length; ob++) {
-        if (_messageListeners[ob] == listener) {
-          _messageListeners.splice (ob,1);
-          break;
+        public function NetConnectionDelegate():void {
+            dispatcher = new Dispatcher();
+            _netConnection = new NetConnection();
+            _netConnection.proxyType = "best";
+            _netConnection.client = this;
+            _netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
+            _netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
+            _netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
+            _netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
         }
-      }
-    }
+
         
-    private function notifyListeners(messageName:String, message:Object):void {
-      if (messageName != null && messageName != "") {
-        for (var notify:String in _messageListeners) {
-          _messageListeners[notify].onMessage(messageName, message);
-        }                
-      } else {
-		  LOGGER.debug("Message name is undefined");
-      }
-    }   
+        public function get connection():NetConnection {
+            return _netConnection;
+        }
+            
+        public function addMessageListener(listener:IMessageListener):void {
+          _messageListeners.push(listener);
+        }
+            
+        public function removeMessageListener(listener:IMessageListener):void {
+          for (var ob:int=0; ob<_messageListeners.length; ob++) {
+            if (_messageListeners[ob] == listener) {
+              _messageListeners.splice (ob,1);
+              break;
+            }
+          }
+        }
+            
+        private function notifyListeners(messageName:String, message:Object):void {
+          if (messageName != null && messageName != "") {
+            for (var notify:String in _messageListeners) {
+              _messageListeners[notify].onMessage(messageName, message);
+            }                
+          } else {
+            LOGGER.debug("Message name is undefined");
+          }
+        }   
+            
+        public function onMessageFromServer(messageName:String, msg:Object):void {
+          if (!authenticated && (messageName == "validateAuthTokenReply")) {
+            handleValidateAuthTokenReply(msg)
+          } else if (messageName == "validateAuthTokenTimedOut") {
+            handleValidateAuthTokenTimedOut(msg)
+          } else if (authenticated) {
+            notifyListeners(messageName, msg);
+          } else {
+            LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]);
+          }     
+        }
+
+        private function validataTokenTimerHandler(event:TimerEvent):void {
+            var logData:Object = UsersUtil.initLogData();
+            logData.tags = ["apps"];
+            logData.key = "validate_token_request_timedout";
+            logData.message = "No response for validate token request.";
+            LOGGER.info(JSON.stringify(logData));
+        }
+
+        private function validateToken():void {
+            var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
+          
+            var message:Object = new Object();
+            message["userId"] = confParams.internalUserID;
+            message["authToken"] = confParams.authToken;
+                    
+            sendMessage(
+                "validateToken",// Remote function name
+                // result - On successful result
+                function(result:Object):void { 
+              
+                },
+                // status - On error occurred
+                function(status:Object):void {
+                    LOGGER.error("Error occurred:");
+                    for (var x:Object in status) {
+                        LOGGER.error(x + " : " + status[x]);
+                    } 
+                },
+                message
+            ); //_netConnection.call      
+            
+            _validateTokenTimer = new Timer(10000, 1);
+            _validateTokenTimer.addEventListener(TimerEvent.TIMER, validataTokenTimerHandler);
+            _validateTokenTimer.start();
+        }
         
-    public function onMessageFromServer(messageName:String, msg:Object):void {
-      if (!authenticated && (messageName == "validateAuthTokenReply")) {
-        handleValidateAuthTokenReply(msg)
-      } else if (messageName == "validateAuthTokenTimedOut") {
-        handleValidateAuthTokenTimedOut(msg)
-      } else if (authenticated) {
-        notifyListeners(messageName, msg);
-      } else {
-        LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]);
-      }     
-    }
-	
-	private function validataTokenTimerHandler(event:TimerEvent):void {
-		var logData:Object = UsersUtil.initLogData();
-        logData.tags = ["apps"];
-		logData.message = "No response for validate token request.";
-		LOGGER.info(JSON.stringify(logData));
-	}
-	
-    private function validateToken():void {
-      var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
-      
-      var message:Object = new Object();
-      message["userId"] = confParams.internalUserID;
-      message["authToken"] = confParams.authToken;
+        private function stopValidateTokenTimer():void {
+            if (_validateTokenTimer != null && _validateTokenTimer.running) {
+                _validateTokenTimer.stop();
+                _validateTokenTimer = null;
+            }
+        }
+
+        private function handleValidateAuthTokenTimedOut(msg: Object):void {  
+            stopValidateTokenTimer();
       
-	  _validateTokenTimer = new Timer(7000, 1);
-	  _validateTokenTimer.addEventListener(TimerEvent.TIMER, validataTokenTimerHandler);
-	  _validateTokenTimer.start();
-	  
-      sendMessage(
-        "validateToken",// Remote function name
-        // result - On successful result
-        function(result:Object):void { 
-          
-        },	
-        // status - On error occurred
-        function(status:Object):void {
-	      LOGGER.error("Error occurred:");
-          for (var x:Object in status) {
-			LOGGER.error(x + " : " + status[x]);
-          } 
-        },
-        message
-      ); //_netConnection.call      
-    }
-    
-	private function stopValidateTokenTimer():void {
-		if (_validateTokenTimer != null && _validateTokenTimer.running) {
-			_validateTokenTimer.stop();
-			_validateTokenTimer = null;
-		}		
-	}
-	
-    private function handleValidateAuthTokenTimedOut(msg: Object):void {  
-      stopValidateTokenTimer();
-	  
-      var map:Object = JSON.parse(msg.msg);  
-      var tokenValid: Boolean = map.valid as Boolean;
-      var userId: String = map.userId as String;
-
-      var logData:Object = UsersUtil.initLogData();
-      logData.tags = ["apps"];
-	  logData.message = "Validate auth token timed out.";
-	  LOGGER.info(JSON.stringify(logData));
-	  
-      if (tokenValid) {
-        authenticated = true;
-      } else {
-        dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
-      }
-	  if (reconnecting) {
-		  onReconnect();
-		  reconnecting = false;
-	  }
-    }
-    
-    private function handleValidateAuthTokenReply(msg: Object):void {  
-      stopValidateTokenTimer();
-		
-      var map:Object = JSON.parse(msg.msg);  
-      var tokenValid: Boolean = map.valid as Boolean;
-      var userId: String = map.userId as String;
+            var map:Object = JSON.parse(msg.msg);  
+            var tokenValid: Boolean = map.valid as Boolean;
+            var userId: String = map.userId as String;
+
+            var logData:Object = UsersUtil.initLogData();
+            logData.tags = ["apps", "connected"];
+            logData.tokenValid = tokenValid;
+            logData.key = "validate_token_response_received";
+            logData.message = "Validate auth token timed out.";
+            LOGGER.info(JSON.stringify(logData));
       
-      if (tokenValid) {
-        authenticated = true;
-      } else {
-        dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
-      }
-	  if (reconnecting) {
-		  onReconnect();
-		  reconnecting = false;
-	  }
-    }
+            if (tokenValid) {
+                authenticated = true;
+            } else {
+                dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
+            }
 
-	private function onReconnect():void {
-		if (authenticated) {
-			onReconnectSuccess();
-		} else {
-			onReconnectFailed();
-		}
-	}
-	
-    private function onReconnectSuccess():void {
-      var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
-      attemptSucceeded.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
-      dispatcher.dispatchEvent(attemptSucceeded);
-    }
+            if (reconnecting) {
+              onReconnect();
+              reconnecting = false;
+            }
+        }
+        
+        private function handleValidateAuthTokenReply(msg: Object):void {  
+            stopValidateTokenTimer();
 
-    private function onReconnectFailed():void {
-      sendUserLoggedOutEvent();
-    }
-    
-    private function sendConnectionSuccessEvent(userid:String):void{      
-      var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS);
-      e.userid = userid;
-      dispatcher.dispatchEvent(e);
+            var map:Object = JSON.parse(msg.msg);  
+            var tokenValid: Boolean = map.valid as Boolean;
+            var userId: String = map.userId as String;
+ 
+            var logData:Object = UsersUtil.initLogData();
+            logData.tags = ["apps", "connected"];
+            logData.tokenValid = tokenValid;
+            logData.status = "validate_token_response_received";
+            logData.message = "Received validate token response from server.";
+            LOGGER.info(JSON.stringify(logData));
+            
+            if (tokenValid) {
+                authenticated = true;
+            } else {
+                dispatcher.dispatchEvent(new InvalidAuthTokenEvent());
+            }
       
-    }
-    
-		public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void {
-			var responder:Responder =	new Responder(                    
-					function(result:Object):void { // On successful result
-						onSuccess("Successfully sent [" + service + "]."); 
-					},	                   
-					function(status:Object):void { // status - On error occurred
-						var errorReason:String = "Failed to send [" + service + "]:\n"; 
-						for (var x:Object in status) { 
-							errorReason += "\t" + x + " : " + status[x]; 
-						} 
-					}
-				);
-			
-			if (message == null) {
-				_netConnection.call(service, responder);			
-			} else {
-				_netConnection.call(service, responder, message);
-			}
-		}
-		
-		/**
-		 * Connect to the server.
-		 * uri: The uri to the conference application.
-		 * username: Fullname of the participant.
-		 * role: MODERATOR/VIEWER
-		 * conference: The conference room
-		 * mode: LIVE/PLAYBACK - Live:when used to collaborate, Playback:when being used to playback a recorded conference.
-		 * room: Need the room number when playing back a recorded conference. When LIVE, the room is taken from the URI.
-		 */
-		public function connect():void {	
-      var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
-			
-      _netConnection = new NetConnection();				
-      _netConnection.proxyType = "best";
-      _netConnection.client = this;
-      _netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus );
-      _netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError );
-      _netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError );
-      _netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError );
+            if (reconnecting) {
+              onReconnect();
+              reconnecting = false;
+            }
+        }
+
+        private function onReconnect():void {
+            if (authenticated) {
+                onReconnectSuccess();
+            } else {
+                onReconnectFailed();
+            }
+        }
+
+        private function onReconnectSuccess():void {
+            var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
+            attemptSucceeded.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
+            dispatcher.dispatchEvent(attemptSucceeded);
+        }
+
+        private function onReconnectFailed():void {
+            sendUserLoggedOutEvent();
+        }
+        
+        private function sendConnectionSuccessEvent(userid:String):void{      
+            var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS);
+            e.userid = userid;
+            dispatcher.dispatchEvent(e);
+        }
+        
+        public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void {
+            var responder:Responder =	new Responder(
+                function(result:Object):void { // On successful result
+                    onSuccess("Successfully sent [" + service + "]."); 
+                },
+                function(status:Object):void { // status - On error occurred
+                    var errorReason:String = "Failed to send [" + service + "]:\n"; 
+                    for (var x:Object in status) { 
+                        errorReason += "\t" + x + " : " + status[x]; 
+                    } 
+                }
+            );
+
+            if (message == null) {
+                _netConnection.call(service, responder);
+            } else {
+                _netConnection.call(service, responder, message);
+            }
+        }
+
+        public function connect():void {
+            var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams();
+
+                
+            try {
+                var appURL:String = BBB.getConfigManager().config.application.uri;
+                var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
+                var result:Array = pattern.exec(appURL);
+            
+                var protocol:String = "rtmp";
+                var uri:String = appURL + "/" + confParams.room;
+            
+                if (BBB.initConnectionManager().isTunnelling) {
+                    bbbAppsUrl = "rtmpt://" + result.server + "/" + result.app + "/" + confParams.room;
+                } else {
+                    bbbAppsUrl = "rtmp://" + result.server + ":1935/" + result.app + "/" + confParams.room;
+                }
+
+                var logData:Object = UsersUtil.initLogData();
+                logData.connection = bbbAppsUrl;
+                logData.tags = ["apps", "connection"];
+                logData.message = "Connecting to bbb-apps.";
+                LOGGER.info(JSON.stringify(logData));
+            
+                connectAttemptCount++;
+                
+                connectionTimer = new Timer(connectAttemptTimeout, 1);
+                connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout);
+                connectionTimer.start();
+
+                _netConnection.connect(bbbAppsUrl, confParams.username, confParams.role,
+                                        confParams.room, confParams.voicebridge, 
+                                        confParams.record, confParams.externUserID,
+                                        confParams.internalUserID, confParams.muteOnStart, confParams.lockSettings);
+                   
+            } catch(e:ArgumentError) {
+                // Invalid parameters.
+                switch (e.errorID) {
+                    case 2004 :
+                        LOGGER.debug("Error! Invalid server location: {0}", [uri]);
+                        break;
+                    default :
+                        LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]);
+                       break;
+                }
+            }
+        }
+            
+        public function connectionTimeout (e:TimerEvent) : void {
+            var logData:Object = UsersUtil.initLogData();
+            logData.connection = bbbAppsUrl;
+            logData.tags = ["apps", "connection"];
+            logData.connectAttemptCount = connectAttemptCount;
+            logData.message = "Connecting attempt to bbb-apps timedout. Retrying.";
+            LOGGER.info(JSON.stringify(logData));
+            
+            if (connectAttemptCount <= maxConnectAttempt) {
+                connect();
+            } else {
+                sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_ATTEMPT_TIMEDOUT);
+            }
+            
+        }
             
-			try {	
-        var appURL:String = BBB.getConfigManager().config.application.uri;
-        var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/;
-        var result:Array = pattern.exec(appURL);
+
+        public function disconnect(logoutOnUserCommand:Boolean):void {
+            this.logoutOnUserCommand = logoutOnUserCommand;
+            _netConnection.close();
+        }
         
-        var protocol:String = "rtmp";
-        var uri:String = appURL + "/" + confParams.room;
+        public function forceClose():void {
+          _netConnection.close();
+        }
         
-        if (BBB.initConnectionManager().isTunnelling) {
-          uri = "rtmpt://" + result.server + "/" + result.app + "/" + confParams.room;
-        } else {
-          uri = "rtmp://" + result.server + ":1935/" + result.app + "/" + confParams.room;
+        protected function netStatus(event:NetStatusEvent):void {
+            handleResult( event );
         }
-				
-				
-				LOGGER.debug("BBB Apps URI=" + uri);
-				
-				_netConnection.connect(uri, confParams.username, confParams.role,
-          confParams.room, confParams.voicebridge, 
-          confParams.record, confParams.externUserID,
-          confParams.internalUserID, confParams.muteOnStart, confParams.lockSettings);
-               
-			} catch(e:ArgumentError) {
-				// Invalid parameters.
-				switch (e.errorID) {
-					case 2004 :
-						LOGGER.debug("Error! Invalid server location: {0}", [uri]);
-						break;						
-					default :
-						LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]);
-					   break;
-				}
-			}	
-		}
-			
-		public function disconnect(logoutOnUserCommand:Boolean):void {
-			this.logoutOnUserCommand = logoutOnUserCommand;
-			_netConnection.close();
-		}
-		
-    
-    public function forceClose():void {
-      _netConnection.close();
-    }
-    
-		protected function netStatus(event:NetStatusEvent):void {
-			handleResult( event );
-		}
-		
-		public function handleResult(event:Object):void {
-			var info : Object = event.info;
-			var statusCode : String = info.code;
+
+        public function handleResult(event:Object):void {
+            var info : Object = event.info;
+            var statusCode : String = info.code;
+            
+            //Stop timeout timer when connected/rejected
+            if (connectionTimer != null && connectionTimer.running) {
+                connectionTimer.stop();
+                connectionTimer = null;
+            }
+            
 
             var logData:Object = UsersUtil.initLogData();
-            logData.tags = ["apps"];
-      
-			switch (statusCode) {
-				case "NetConnection.Connect.Success":
-					numNetworkChangeCount = 0;
-                    logData.message = "Successfully connected to BBB App.";
+            logData.tags = ["apps", "connection"];
+          
+            switch (statusCode) {
+                case "NetConnection.Connect.Success":
+                    numNetworkChangeCount = 0;
+                    connectAttemptCount = 0;
+                    logData.message = "Successfully connected to bbb-apps.";
                     LOGGER.info(JSON.stringify(logData));
                     validateToken();
-					break;
-			
-				case "NetConnection.Connect.Failed":					
-                    logData.message = "Connection to bbb-apps failed, even when tunneling.";
+                    break;
+
+                case "NetConnection.Connect.Failed":
+                    logData.message = "Connection to bbb-apps failed.";
+                    LOGGER.info(JSON.stringify(logData));
+                    sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);	
+                    break;
+
+                case "NetConnection.Connect.Closed":
+                    logData.message = "NetConnection.Connect.Closed on bbb-apps";
                     LOGGER.info(JSON.stringify(logData));
-					sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED);								
-					break;
-					
-				case "NetConnection.Connect.Closed":	
-					logData.message = "NetConnection.Connect.Closed on bbb-apps";
-					LOGGER.info(JSON.stringify(logData));
-                    sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED);		
-					break;
-					
-				case "NetConnection.Connect.InvalidApp":
+                    sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED);
+                    break;
+
+                case "NetConnection.Connect.InvalidApp":
                     logData.message = "bbb-app not found.";
                     LOGGER.info(JSON.stringify(logData));
-					sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP);				
-					break;
-					
-				case "NetConnection.Connect.AppShutDown":
+                    sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP);
+                    break;
+
+                case "NetConnection.Connect.AppShutDown":
                     LOGGER.debug(":viewers application has been shutdown");
-					sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN);	
-					break;
-					
-				case "NetConnection.Connect.Rejected":
-          var appURL:String = BBB.getConfigManager().config.application.uri
-          LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [appURL]);
-					sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED);		
-					break;
-				
-				case "NetConnection.Connect.NetworkChange":
-					numNetworkChangeCount++;
-					if (numNetworkChangeCount % 20 == 0) {
-						logData.message = "Detected network change on bbb-apps";
-						logData.numNetworkChangeCount = numNetworkChangeCount;
-						LOGGER.info(JSON.stringify(logData));
-					}
-					break;
-					
-				default :
-          LOGGER.debug(":Default status to the viewers application" );
-				   sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
-				   break;
-			}
-		}
-			
-		protected function netSecurityError(event: SecurityErrorEvent):void {
+                    sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN);
+                    break;
+
+                case "NetConnection.Connect.Rejected":
+                    var appURL:String = BBB.getConfigManager().config.application.uri
+                    LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [appURL]);
+                    sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED);
+                    break;
+                
+                case "NetConnection.Connect.NetworkChange":
+                    numNetworkChangeCount++;
+                    if (numNetworkChangeCount % 20 == 0) {
+                        logData.message = "Detected network change on bbb-apps";
+                        logData.numNetworkChangeCount = numNetworkChangeCount;
+                        LOGGER.info(JSON.stringify(logData));
+                    }
+                    break;
+
+                default :
+                    LOGGER.debug(":Default status to the viewers application" );
+                    sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
+                    break;   
+             }
+        }
+
+        protected function netSecurityError(event: SecurityErrorEvent):void {
             var logData:Object = UsersUtil.initLogData();
-            logData.tags = ["apps"];
+            logData.tags = ["apps", "connection"];
             logData.message = "Security error - " + event.text;
             LOGGER.info(JSON.stringify(logData));
-			sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
-		}
-		
-		protected function netIOError(event: IOErrorEvent):void {
+            sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
+        }
+
+        protected function netIOError(event: IOErrorEvent):void {
             var logData:Object = UsersUtil.initLogData();
-            logData.tags = ["apps"];
+            logData.tags = ["apps", "connection"];
             logData.message = "Input/output error - " + event.text;
             LOGGER.info(JSON.stringify(logData));
 
-			sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
-		}
-			
-		protected function netASyncError(event: AsyncErrorEvent):void  {
-	  		LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
-			sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
-		}	
-			
+            sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
+        }
+
+        protected function netASyncError(event: AsyncErrorEvent):void  {
+            LOGGER.debug("Asynchronous code error - {0}", [event.toString()]);
+            sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON);
+        }
+
         private function sendConnectionFailedEvent(reason:String):void{
             var logData:Object = UsersUtil.initLogData();
-            logData.tags = ["apps"];
+            logData.tags = ["apps", "connection"];
 
             if (this.logoutOnUserCommand) {
                 logData.reason = "User requested.";
                 logData.message = "User logged out from BBB App.";
                 LOGGER.info(JSON.stringify(logData));
-                
+                   
                 sendUserLoggedOutEvent();
             } else if (reason == ConnectionFailedEvent.CONNECTION_CLOSED && !UsersUtil.isUserEjected()) {
                 // do not try to reconnect if the connection failed is different than CONNECTION_CLOSED  
@@ -400,25 +439,26 @@ package org.bigbluebutton.main.model.users
                 LOGGER.info(JSON.stringify(logData));
 
                 if (reconnecting) {
-                  var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
-                  attemptFailedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
-                  dispatcher.dispatchEvent(attemptFailedEvent);
+                    var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
+                    attemptFailedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
+                    dispatcher.dispatchEvent(attemptFailedEvent);
                 } else {
-                  reconnecting = true;
-                  authenticated = false;
-
-                  var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
-                  disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
-                  disconnectedEvent.payload.callback = connect;
-                  disconnectedEvent.payload.callbackParameters = new Array();
-                  dispatcher.dispatchEvent(disconnectedEvent);
-                }
+                    reconnecting = true;
+                    authenticated = false;
 
+                    var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
+                    disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION;
+                    disconnectedEvent.payload.callback = connect;
+                    disconnectedEvent.payload.callbackParameters = new Array();
+                    dispatcher.dispatchEvent(disconnectedEvent);
+                }
             } else {
                 if (UsersUtil.isUserEjected()) {
                     logData.message = "User has been ejected from meeting.";
                     LOGGER.info(JSON.stringify(logData));
                     reason = ConnectionFailedEvent.USER_EJECTED_FROM_MEETING;
+                    var cfe:ConnectionFailedEvent = new ConnectionFailedEvent(reason);
+                    dispatcher.dispatchEvent(cfe);
                 } else {
                     logData.message = "Connection failed event - " + reason;
                     LOGGER.info(JSON.stringify(logData));
@@ -428,22 +468,22 @@ package org.bigbluebutton.main.model.users
 
             }
         }
-		
-		private function sendUserLoggedOutEvent():void{
-			var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT);
-			dispatcher.dispatchEvent(e);
-		}
-		
-		public function onBWCheck(... rest):Number { 
-			return 0; 
-		} 
-    
-		public function onBWDone(... rest):void { 
-			var p_bw:Number; 
-			if (rest.length > 0) p_bw = rest[0]; 
-			// your application should do something here 
-			// when the bandwidth check is complete 
-			LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); 
-		}
-	}
+
+        private function sendUserLoggedOutEvent():void{
+            var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT);
+            dispatcher.dispatchEvent(e);
+        }
+
+        public function onBWCheck(... rest):Number { 
+            return 0; 
+        } 
+        
+        public function onBWDone(... rest):void { 
+            var p_bw:Number; 
+            if (rest.length > 0) p_bw = rest[0]; 
+            // your application should do something here 
+            // when the bandwidth check is complete 
+            LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); 
+        }
+    }
 }
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as
index 6ceb8b8efd29258a32fa63ddcc7f523a8df3af4e..f6b8530e5a99d5138daeff5fc225127aadf836be 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as
@@ -18,23 +18,22 @@
 */
 package org.bigbluebutton.main.model.users.events
 {
-	import flash.events.Event;
+    import flash.events.Event;
 
-	public class ConnectionFailedEvent extends Event
-	{
-		public static const UNKNOWN_REASON:String = "unknownReason";
-		public static const CONNECTION_FAILED:String = "connectionFailed";
-		public static const CONNECTION_CLOSED:String = "connectionClosed";
-		public static const INVALID_APP:String = "invalidApp";
-		public static const APP_SHUTDOWN:String = "appShutdown";
-		public static const CONNECTION_REJECTED:String = "connectionRejected";
-		public static const ASYNC_ERROR:String = "asyncError";
-		public static const USER_LOGGED_OUT:String = "userHasLoggedOut";
-		public static const USER_EJECTED_FROM_MEETING:String = "userHasBeenEjectFromMeeting";
-		
-		public function ConnectionFailedEvent(type:String)
-		{
-			super(type, true, false);
-		}
-	}
+    public class ConnectionFailedEvent extends Event {
+        public static const UNKNOWN_REASON:String = "unknownReason";
+        public static const CONNECTION_FAILED:String = "connectionFailed";
+        public static const CONNECTION_CLOSED:String = "connectionClosed";
+        public static const CONNECTION_ATTEMPT_TIMEDOUT:String = "connectionAttemptTimedout";
+        public static const INVALID_APP:String = "invalidApp";
+        public static const APP_SHUTDOWN:String = "appShutdown";
+        public static const CONNECTION_REJECTED:String = "connectionRejected";
+        public static const ASYNC_ERROR:String = "asyncError";
+        public static const USER_LOGGED_OUT:String = "userHasLoggedOut";
+        public static const USER_EJECTED_FROM_MEETING:String = "userHasBeenEjectFromMeeting";
+
+        public function ConnectionFailedEvent(type:String) {
+            super(type, true, false);
+        }
+    }
 }
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
index 81a554556abb2bf31dea39a2ffcb36abcb4a7d42..282b3920d35020c194a2e4700152e57dd9c5b4e6 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
@@ -180,7 +180,14 @@ package org.bigbluebutton.modules.users.services
     }
 
     private function handleUserEjectedFromMeeting(msg: Object):void {
-      UsersUtil.setUserEjected();
+        UsersUtil.setUserEjected();
+        var logData:Object = UsersUtil.initLogData();
+        logData.tags = ["users"];
+        logData.status = "user_ejected";
+        logData.message = "User ejected from meeting.";
+
+        LOGGER.info(JSON.stringify(logData));
+      
     }
 
 	private function handleUserLocked(msg:Object):void {
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js
index 65e2e45fdc65e7986534990e60cb998e17e54d44..7cd4111d0aba543ff55ed5d5fa45a10583c5a9bf 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js
@@ -6,7 +6,7 @@ import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearU
 import clearChats from '/imports/api/chat/server/modifiers/clearChats';
 import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
 import clearSlides from '/imports/api/slides/server/modifiers/clearSlides';
-import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection';
+import clearPoll from '/imports/api/polls/server/modifiers/clearPoll';
 import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection';
 import { clearCaptionsCollection }
   from '/imports/api/captions/server/modifiers/clearCaptionsCollection';
@@ -17,8 +17,8 @@ export default function clearMeetings() {
     clearCaptionsCollection();
     clearChats();
     clearCursorCollection();
-    clearPollCollection();
     clearPresentations();
+    clearPoll();
     clearShapesCollection();
     clearSlides();
     clearUsersCollection();
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js
index d3379a15f9bf197316763e50adf5245b9ee169ee..8ae41ea02b0c52eabb6ac2fdb95d3c1b3b85bd12 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js
@@ -6,7 +6,7 @@ import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearU
 import clearChats from '/imports/api/chat/server/modifiers/clearChats';
 import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
 import clearSlides from '/imports/api/slides/server/modifiers/clearSlides';
-import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection';
+import clearPoll from '/imports/api/polls/server/modifiers/clearPoll';
 import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection';
 import { clearCaptionsCollection }
   from '/imports/api/captions/server/modifiers/clearCaptionsCollection';
@@ -28,8 +28,8 @@ export default function removeMeeting(meetingId) {
       clearCaptionsCollection(meetingId);
       clearChats(meetingId);
       clearCursorCollection(meetingId);
-      clearPollCollection(meetingId);
       clearPresentations(meetingId);
+      clearPoll(meetingId);
       clearShapesCollection(meetingId);
       clearSlides(meetingId);
       clearUsersCollection(meetingId);
diff --git a/bigbluebutton-html5/imports/api/polls/index.js b/bigbluebutton-html5/imports/api/polls/index.js
old mode 100755
new mode 100644
diff --git a/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js b/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js
new file mode 100644
index 0000000000000000000000000000000000000000..c20b8f971f4bfe306867ae254cc03f4923184ec8
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js
@@ -0,0 +1,9 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import handlePollStopped from './handlers/pollStopped';
+import handlePollStarted from './handlers/pollStarted';
+import handleUserVoted from './handlers/userVoted';
+
+RedisPubSub.on('poll_show_result_message', handlePollStopped);
+RedisPubSub.on('poll_started_message', handlePollStarted);
+RedisPubSub.on('poll_stopped_message', handlePollStopped);
+RedisPubSub.on('user_voted_poll_message', handleUserVoted);
diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js
new file mode 100644
index 0000000000000000000000000000000000000000..68d312d641ba9248d274cf66975c974bd7d4c288
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js
@@ -0,0 +1,16 @@
+import { check } from 'meteor/check';
+import addPoll from '../modifiers/addPoll';
+
+export default function pollStarted({ payload }) {
+  check(payload, Object);
+
+  const meetingId = payload.meeting_id;
+  const requesterId = payload.requester_id;
+  const poll = payload.poll;
+
+  check(meetingId, String);
+  check(requesterId, String);
+  check(poll, Object);
+
+  return addPoll(meetingId, requesterId, poll);
+}
diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js
new file mode 100644
index 0000000000000000000000000000000000000000..8705a6b22b54072093b886e9d367001af6621c45
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js
@@ -0,0 +1,22 @@
+import { check } from 'meteor/check';
+import removePoll from '../modifiers/removePoll';
+import clearPolls from '../modifiers/clearPolls';
+
+export default function pollStopped({ payload }) {
+  check(payload, Object);
+
+  const meetingId = payload.meeting_id;
+  const poll = payload.poll;
+
+  check(meetingId, String);
+
+  if (poll) {
+    const pollId = poll.id;
+
+    check(pollId, String);
+
+    return removePoll(meetingId, pollId);
+  }
+
+  return clearPolls(meetingId);
+}
diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js
new file mode 100644
index 0000000000000000000000000000000000000000..29261be9082d48f8738c033bb9178e59e2a6f675
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js
@@ -0,0 +1,16 @@
+import { check } from 'meteor/check';
+import updateVotes from '../modifiers/updateVotes';
+
+export default function userVoted({ payload }) {
+  check(payload, Object);
+
+  const meetingId = payload.meeting_id;
+  const poll = payload.poll;
+  const requesterId = payload.presenter_id;
+
+  check(meetingId, String);
+  check(poll, Object);
+  check(requesterId, String);
+
+  return updateVotes(poll, meetingId, requesterId);
+};
diff --git a/bigbluebutton-html5/imports/api/polls/server/index.js b/bigbluebutton-html5/imports/api/polls/server/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..92451ac76bf27410726e8f3cd2eebac46cd7b83e
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/index.js
@@ -0,0 +1,3 @@
+import './eventHandlers';
+import './methods';
+import './publishers';
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods.js b/bigbluebutton-html5/imports/api/polls/server/methods.js
new file mode 100644
index 0000000000000000000000000000000000000000..4136d79f73d2d95adb61d39aa729744b17a882f4
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/methods.js
@@ -0,0 +1,6 @@
+import { Meteor } from 'meteor/meteor';
+import publishVote from './methods/publishVote';
+
+Meteor.methods({
+  publishVote,
+});
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js
new file mode 100644
index 0000000000000000000000000000000000000000..5f7f545e4f1bd5fffc6a79bd0fc8db8450db55cc
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js
@@ -0,0 +1,61 @@
+import { isAllowedTo } from '/imports/startup/server/userPermissions';
+import RedisPubSub from '/imports/startup/server/redis';
+import { check } from 'meteor/check';
+import Polls from '/imports/api/polls';
+import Logger from '/imports/startup/server/logger';
+
+export default function publishVote(credentials, pollId, pollAnswerId) { //TODO discuss location
+  const REDIS_CONFIG = Meteor.settings.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toBBBApps.polling;
+  const EVENT_NAME = 'vote_poll_user_request_message';
+
+  if (!isAllowedTo('subscribePoll', credentials)) {
+    throw new Meteor.Error('not-allowed', `You are not allowed to publishVote`);
+  }
+
+  const { meetingId, requesterUserId, requesterToken } = credentials;
+
+  const currentPoll = Polls.findOne({
+    users: requesterUserId,
+    meetingId: meetingId,
+    'poll.answers.id': pollAnswerId,
+    'poll.id': pollId,
+  });
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(pollAnswerId, Number);
+  check(currentPoll.meetingId, String);
+
+  let payload = {
+    meeting_id: currentPoll.meetingId,
+    user_id: requesterUserId,
+    poll_id: currentPoll.poll.id,
+    question_id: 0,
+    answer_id: pollAnswerId,
+  };
+
+  const selector = {
+    users: requesterUserId,
+    meetingId: meetingId,
+    'poll.answers.id': pollAnswerId,
+  };
+
+  const modifier = {
+    $pull: {
+      users: requesterUserId,
+    },
+  };
+
+  const cb = (err, numChanged) => {
+    if (err) {
+      return Logger.error(`Updating Polls collection: ${err}`);
+    }
+
+    Logger.info(`Updating Polls collection (meetingId: ${meetingId},
+                                            pollId: ${currentPoll.poll.id}!)`);
+  };
+
+  Polls.update(selector, modifier, cb);
+  return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
+}
diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js
deleted file mode 100755
index 9e88c0505d51b19cfc068bf130f191eba5381aff..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { publish } from '/imports/api/common/server/helpers';
-import { isAllowedTo } from '/imports/startup/server/userPermissions';
-import { appendMessageHeader } from '/imports/api/common/server/helpers';
-import Polls from '/imports/api/polls';
-import { logger } from '/imports/startup/server/logger';
-
-Meteor.methods({
-  publishVoteMessage(credentials, pollId, pollAnswerId) { //TODO discuss location
-    const REDIS_CONFIG = Meteor.settings.redis;
-    if (isAllowedTo('subscribePoll', credentials)) {
-      const { meetingId, requesterUserId, requesterToken } = credentials;
-      const eventName = 'vote_poll_user_request_message';
-
-      const result = Polls.findOne({
-        users: requesterUserId,
-        meetingId: meetingId,
-        'poll.answers.id': pollAnswerId,
-        'poll.id': pollId,
-      });
-
-      if ((meetingId != null) &&
-        (result.meetingId != null) &&
-        (requesterUserId != null) &&
-        (pollAnswerId != null)) {
-        let message = {
-          payload: {
-            meeting_id: result.meetingId,
-            user_id: requesterUserId,
-            poll_id: result.poll.id,
-            question_id: 0,
-            answer_id: pollAnswerId,
-          },
-        };
-        Polls.update({
-          users: requesterUserId,
-          meetingId: meetingId,
-          'poll.answers.id': pollAnswerId,
-        }, {
-          $pull: {
-            users: requesterUserId,
-          },
-        });
-        message = appendMessageHeader(eventName, message);
-        logger.info('publishing Poll response to redis');
-        return publish(REDIS_CONFIG.channels.toBBBApps.polling, message);
-      }
-    }
-  },
-});
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea091c7b82dfcce584eb2bd7689125e098c84458
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js
@@ -0,0 +1,54 @@
+import Meetings from '/imports/api/meetings';
+import Users from '/imports/api/users';
+import Polls from '/imports/api/polls';
+import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
+
+export default function addPoll(meetingId, requesterId, poll) {
+  check(poll, Object);
+  check(requesterId, String);
+  check(meetingId, String);
+
+  let selector = {
+    meetingId: meetingId,
+  };
+
+  const options = {
+    fields: {
+      'user.userid': 1,
+      _id: 0,
+    },
+  };
+
+  const userIds = Users.find(selector, options)
+                       .fetch()
+                       .map(user => user.user.userid);
+
+  selector = {
+    meetingId,
+    requester: requesterId,
+    'poll.id': poll.id,
+  };
+
+  const modifier = {
+    meetingId,
+    poll,
+    requester: requesterId,
+    users: userIds,
+  };
+
+  const cb = (err, numChanged) => {
+    if (err != null) {
+      return Logger.error(`Adding Poll to collection: ${poll.id}`);
+    }
+
+    const { insertedId } = numChanged;
+    if (insertedId) {
+      return Logger.info(`Added Poll id=${poll.id}`);
+    }
+
+    return Logger.info(`Upserted Poll id=${poll.id}`);
+  };
+
+  return Polls.upsert(selector, modifier, cb);
+};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js
deleted file mode 100755
index 6ac5fb87e1749c476687afd3c26d89038e42b8d9..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import Polls from '/imports/api/polls';
-import { logger } from '/imports/startup/server/logger';
-
-export function addPollToCollection(poll, requesterId, users, meetingId) {
-  // copying all the userids into an array
-  let newUsers = [];
-  newUsersLength = users.length;
-  for (let i = 0; i < newUsersLength; i++) {
-    const user = users[i];
-    newUsers.push(user.user.userid);
-  }
-
-  // adding the initial number of votes for each answer
-  // _answers = poll.answers;
-  // _answers_length = _answers.length;
-  // for (j = 0; j < _answers_length; j++) {
-  //   answer = _answers[j];
-  //   answer.num_votes = 0;
-  // }
-
-  // adding the initial number of responders and respondents to the poll, which will be displayed
-  // for presenter (in HTML5 client) when they start the poll
-  numResponders = -1;
-  numRespondents = -1;
-
-  // adding all together and inserting into the Polls collection
-  const entry = {
-    meetingId: meetingId,
-    poll: poll,
-    requester: requesterId,
-    users: newUsers,
-    num_responders: -1,
-    num_respondents: -1,
-  };
-  logger.info(`added poll _id=[${poll.id}]:meetingId=[${meetingId}].`);
-  return Polls.insert(entry);
-};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js
deleted file mode 100755
index 5070cb700f616c9bc14b3f1475cf6b00cfe7a044..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Polls from '/imports/api/polls';
-import { logger } from '/imports/startup/server/logger';
-
-export function clearPollCollection() {
-  const meetingId = arguments[0];
-  const pollId = arguments[1];
-
-  //TODO make it so you can delete the polls based only on meetingId
-  if (meetingId != null && pollId != null) {
-    return Polls.remove({
-      meetingId: meetingId,
-      'poll.id': pollId,
-    }, logger.info(`cleared Polls Collection (meetingId: ${meetingId}, pollId: ${pollId}!)`));
-  } else {
-    return Polls.remove({}, logger.info('cleared Polls Collection (all meetings)!'));
-  }
-};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js
new file mode 100644
index 0000000000000000000000000000000000000000..7e4ea4a1ea7b2ecfa23788f817f0345522a07fcc
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js
@@ -0,0 +1,10 @@
+import Polls from '/imports/api/polls';
+import Logger from '/imports/startup/server/logger';
+
+export default function clearPolls(meetingId) {
+  if (meetingId) {
+    return Polls.remove({ meetingId, }, Logger.info(`Cleared Polls (${meetingId})`));
+  }
+
+  return Polls.remove({}, Logger.info('Cleared Polls (all)'));
+};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js
deleted file mode 100644
index 20bbc4c92ca13b4e8fd9997c2b5b20ac2fcd12bb..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { eventEmitter } from '/imports/startup/server';
-import { clearPollCollection } from './clearPollCollection';
-import { updatePollCollection } from './updatePollCollection';
-import { addPollToCollection } from './addPollToCollection';
-import Meetings from '/imports/api/meetings';
-import Users from '/imports/api/users';
-
-eventEmitter.on('poll_show_result_message', function (arg) {
-  const payload = arg.payload;
-  const meetingId = payload.meeting_id;
-  if (payload != null && payload.poll != null && payload.poll.id != null && meetingId != null) {
-    const pollId = payload.poll.id;
-    clearPollCollection(meetingId, pollId);
-  }
-
-  return arg.callback();
-});
-
-eventEmitter.on('poll_started_message', function (arg) {
-  const payload = arg.payload;
-  const meetingId = payload.meeting_id;
-
-  if (payload != null && meetingId != null &&
-    payload.requester_id != null && payload.poll != null) {
-    if (Meetings.findOne({
-        meetingId: meetingId,
-      }) != null) {
-      const users = Users.find({
-        meetingId: meetingId,
-      }, {
-        fields: {
-          'user.userid': 1,
-          _id: 0,
-        },
-      }).fetch();
-      addPollToCollection(
-        payload.poll,
-        payload.requester_id,
-        users,
-        meetingId
-      );
-    }
-  }
-
-  return arg.callback();
-});
-
-eventEmitter.on('poll_stopped_message', function (arg) {
-  const payload = arg.payload;
-  const meetingId = payload.meeting_id;
-
-  if (meetingId != null && payload != null && payload.poll_id != null) {
-    const pollId = payload.poll_id;
-    clearPollCollection(meetingId, pollId);
-  }
-
-  return arg.callback();
-});
-
-eventEmitter.on('user_voted_poll_message', function (arg) {
-  const payload = arg.payload;
-  const meetingId = payload.meeting_id;
-  if (payload != null && payload.poll != null && meetingId != null &&
-    payload.presenter_id != null) {
-    const pollObj = payload.poll;
-    const requesterId = payload.presenter_id;
-    updatePollCollection(pollObj, meetingId, requesterId);
-    return arg.callback();
-  }
-});
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js
new file mode 100644
index 0000000000000000000000000000000000000000..f5af42d8febab5cf649878a77f0cbde1ad8ff16c
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js
@@ -0,0 +1,25 @@
+import Polls from '/imports/api/polls';
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+
+export default function removePoll(meetingId, pollId) {
+  check(meetingId, String);
+  check(pollId, String);
+
+  const selector = {
+    meetingId,
+    'poll.id': pollId,
+  };
+
+  const cb = (err, numChanged) => {
+    if (err) {
+      return Logger.error(`Removing Poll from collection: ${err}`);
+    }
+
+    if (numChanged) {
+      return Logger.info(`Removed Poll id=${pollId}`);
+    }
+  };
+
+  return Polls.remove(selector, cb);
+};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js
deleted file mode 100755
index e224c5932f9a99d645c42fa441ade80459e9e183..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import Polls from '/imports/api/polls';
-import { logger } from '/imports/startup/server/logger';
-
-export function updatePollCollection(poll, meetingId, requesterId) {
-  if ((poll.answers != null) && (poll.numResponders != null) && (poll.numRespondents != null) &&
-    (poll.id != null) && (meetingId != null) && (requesterId != null)) {
-    return Polls.update({
-      meetingId: meetingId,
-      requester: requesterId,
-      poll: { id: poll.id },
-    }, {
-      $set: {
-        poll: { answers: poll.answers },
-        poll: { num_responders: poll.numResponders },
-        poll: { num_respondents: poll.numRespondents },
-      },
-    }, logger.info(`updating Polls Collection (meetingId: ${meetingId}, pollId: ${poll.id}!)`));
-  }
-};
diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js
new file mode 100644
index 0000000000000000000000000000000000000000..540f58373eb43d75efe2109541e18811ddd3e9d0
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js
@@ -0,0 +1,49 @@
+import Polls from '/imports/api/polls';
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+
+export default function updateVotes(poll, meetingId, requesterId) {
+  check(meetingId, String);
+  check(requesterId, String);
+  check(poll, Object);
+
+  const {
+    id,
+    answers,
+  } = poll;
+
+  const numResponders = poll.num_responders;
+  const numRespondents = poll.num_respondents;
+
+  check(id, String);
+  check(answers, Array);
+
+  check(numResponders, Number);
+  check(numRespondents, Number);
+
+  const selector = {
+    meetingId,
+    requester: requesterId,
+    'poll.id': id,
+  };
+
+  const modifier = {
+    $set: {
+      poll: {
+        answers: answers,
+        num_responders: numResponders,
+        num_respondents: numRespondents,
+      },
+    },
+  };
+
+  const cb = (err, numChanged) => {
+    if (err) {
+      return Logger.error(`Updating Polls collection: ${err}`);
+    }
+
+    Logger.info(`Updating Polls collection (meetingId: ${meetingId}, pollId: ${id}!)`);
+  };
+
+  return Polls.update(selector, modifier, cb);
+};
diff --git a/bigbluebutton-html5/imports/api/polls/server/publications.js b/bigbluebutton-html5/imports/api/polls/server/publications.js
deleted file mode 100755
index 7092ebe1beca24c6f2eaaaf468609ec6ae15f5c8..0000000000000000000000000000000000000000
--- a/bigbluebutton-html5/imports/api/polls/server/publications.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { isAllowedTo } from '/imports/startup/server/userPermissions';
-import Polls from '/imports/api/polls';
-import { logger } from '/imports/startup/server/logger';
-
-Meteor.publish('polls', function (credentials) {
-  const { meetingId, requesterUserId, requesterToken } = credentials;
-
-  //checking if it is allowed to see Poll Collection in general
-  if (isAllowedTo('subscribePoll', credentials)) {
-    //checking if it is allowed to see a number of votes (presenter only)
-    if (isAllowedTo('subscribeAnswers', credentials)) {
-      logger.info('publishing Poll for presenter: ' + meetingId + ' ' + requesterUserId + ' ' +
-        requesterToken);
-      return Polls.find({
-        meetingId: meetingId,
-        users: requesterUserId,
-      });
-    } else {
-      logger.info('publishing Poll for viewer: ' + meetingId + ' ' + requesterUserId + ' ' +
-        requesterToken);
-      return Polls.find({
-        meetingId: meetingId,
-        users: requesterUserId,
-      }, {
-        fields: {
-          'poll.answers.num_votes': 0,
-        },
-      });
-    }
-  } else {
-    this.error(new Meteor.Error(402, "The user was not authorized to subscribe for 'polls'"));
-  }
-});
diff --git a/bigbluebutton-html5/imports/api/polls/server/publishers.js b/bigbluebutton-html5/imports/api/polls/server/publishers.js
new file mode 100644
index 0000000000000000000000000000000000000000..930e15edcb4994a75c53e9a041fe2d1ced67d21d
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/polls/server/publishers.js
@@ -0,0 +1,35 @@
+import { Meteor } from 'meteor/meteor';
+import { isAllowedTo } from '/imports/startup/server/userPermissions';
+import Polls from '/imports/api/polls';
+import { check } from 'meteor/check';
+import { logger } from '/imports/startup/server/logger';
+
+Meteor.publish('polls', (credentials) => {
+  //checking if it is allowed to see Poll Collection in general
+  if (!isAllowedTo('subscribePoll', credentials)) {
+    this.error(new Meteor.Error(402, "The user was not authorized to subscribe for 'polls'"));
+  }
+
+  const { meetingId, requesterUserId, requesterToken } = credentials;
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(requesterToken, String);
+
+  const selector = {
+    meetingId: meetingId,
+    users: requesterUserId,
+  };
+
+  let options = null;
+
+  if (!isAllowedTo('subscribeAnswers', credentials)) {
+    options = {
+      fields: {
+        'poll.answers.num_votes': 0,
+      },
+    };
+  }
+
+  return Polls.find(selector, options);
+});
diff --git a/bigbluebutton-html5/imports/ui/components/polling/service.js b/bigbluebutton-html5/imports/ui/components/polling/service.js
index 6741436dd6d4a199a28340e4f9787dad51645b8f..a2fc2746f2ac81b5a8b46a3cbfc3b6f461c056b8 100755
--- a/bigbluebutton-html5/imports/ui/components/polling/service.js
+++ b/bigbluebutton-html5/imports/ui/components/polling/service.js
@@ -17,7 +17,7 @@ let mapPolls = function () {
     pollExists: true,
     amIRequester: amIRequester,
     handleVote: function (pollId, answerId) {
-      callServer('publishVoteMessage', pollId, answerId.id);
+      callServer('publishVote', pollId, answerId.id);
     },
   };
 };
diff --git a/bigbluebutton-html5/server/main.js b/bigbluebutton-html5/server/main.js
index 6d0a06ca788dcd2ab505c32a22fe2cf2b6971d67..d1256192fe2f115b164ba056648bc8d26f0c61ab 100755
--- a/bigbluebutton-html5/server/main.js
+++ b/bigbluebutton-html5/server/main.js
@@ -17,12 +17,7 @@ import '/imports/api/meetings/server';
 
 import '/imports/api/phone/server/modifiers/eventHandlers';
 
-import '/imports/api/polls/server/publications';
-import '/imports/api/polls/server/methods/publishVoteMessage';
-import '/imports/api/polls/server/modifiers/addPollToCollection';
-import '/imports/api/polls/server/modifiers/clearPollCollection';
-import '/imports/api/polls/server/modifiers/updatePollCollection';
-import '/imports/api/polls/server/modifiers/eventHandlers';
+import '/imports/api/polls/server';
 
 import '/imports/api/presentations/server';
 
diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
index cb9a9ffbfb21087aa9285e888ad4fcad7c672d51..01e641cd98585163e011b320640b1f20eb256f61 100755
--- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
+++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy
@@ -18,6 +18,8 @@
  */
 package org.bigbluebutton.web.controllers
 
+import com.google.gson.Gson
+
 import javax.servlet.ServletRequest;
 
 import java.net.URI;
@@ -1455,7 +1457,7 @@ class ApiController {
       reject = true
     } else {
       sessionToken = StringUtils.strip(params.sessionToken)
-      log.info("SessionToken = " + sessionToken)
+      log.info("Getting ConfigXml for SessionToken = " + sessionToken)
       if (!session[sessionToken]) {
           reject = true
       } else {
@@ -1478,6 +1480,20 @@ class ApiController {
         }
       }
     } else {
+      Map<String, Object> logData = new HashMap<String, Object>();
+      logData.put("meetingId", us.meetingID);
+      logData.put("externalMeetingId", us.externMeetingID);
+      logData.put("name", us.fullname);
+      logData.put("userId", us.internalUserId);
+      logData.put("sessionToken", sessionToken);
+      logData.put("message", "handle_configxml_api");
+      logData.put("description", "Handling ConfigXml API.");
+
+      Gson gson = new Gson();
+      String logStr = gson.toJson(logData);
+
+      log.info(logStr);
+
       response.addHeader("Cache-Control", "no-cache")
       render text: us.configXML, contentType: 'text/xml'
     }
@@ -1544,7 +1560,20 @@ class ApiController {
       // how many times a user reconnects or refresh the browser.
       String newInternalUserID = us.internalUserId + "_" + us.incrementConnectionNum()
 
-      log.info("Found conference for " + us.fullname)
+      Map<String, Object> logData = new HashMap<String, Object>();
+      logData.put("meetingId", us.meetingID);
+      logData.put("externalMeetingId", us.externMeetingID);
+      logData.put("name", us.fullname);
+      logData.put("userId", newInternalUserID);
+      logData.put("sessionToken", sessionToken);
+      logData.put("message", "handle_enter_api");
+      logData.put("description", "Handling ENTER API.");
+
+      Gson gson = new Gson();
+      String logStr = gson.toJson(logData);
+
+      log.info(logStr);
+
       response.addHeader("Cache-Control", "no-cache")
       withFormat {
         json {
diff --git a/record-and-playback/core/Gemfile.lock b/record-and-playback/core/Gemfile.lock
index 16e319e4073c5e1eae440674a67af97b779b217e..c708d7d2f809817332301df0c40978d97a9fbb57 100644
--- a/record-and-playback/core/Gemfile.lock
+++ b/record-and-playback/core/Gemfile.lock
@@ -2,37 +2,41 @@ GEM
   remote: http://rubygems.org/
   specs:
     absolute_time (1.0.0)
-    addressable (2.3.6)
+    addressable (2.4.0)
     builder (3.2.2)
-    curb (0.8.6)
-    fastimage (1.6.4)
-      addressable (~> 2.3, >= 2.3.5)
-    mime-types (2.4.3)
-    mini_portile (0.6.1)
+    curb (0.9.3)
+    fastimage (2.0.0)
+      addressable (~> 2)
+    mime-types (2.6.2)
+    mini_portile2 (2.1.0)
     mono_logger (1.1.0)
-    multi_json (1.10.1)
-    nokogiri (1.6.4.1)
-      mini_portile (~> 0.6.0)
+    multi_json (1.12.1)
+    nokogiri (1.6.8)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
     open4 (1.3.4)
-    rack (1.5.2)
+    pkg-config (1.1.7)
+    rack (1.6.4)
     rack-protection (1.5.3)
       rack
-    redis (3.1.0)
-    redis-namespace (1.5.1)
+    redis (3.3.1)
+    redis-namespace (1.5.2)
       redis (~> 3.0, >= 3.0.4)
-    resque (1.25.2)
+    resque (1.26.0)
       mono_logger (~> 1.0)
       multi_json (~> 1.0)
       redis-namespace (~> 1.3)
       sinatra (>= 0.9.2)
       vegas (~> 0.1.2)
-    rubyzip (1.1.6)
-    sinatra (1.4.5)
-      rack (~> 1.4)
+    rubyzip (1.2.0)
+    sinatra (1.4.7)
+      rack (~> 1.5)
       rack-protection (~> 1.4)
-      tilt (~> 1.3, >= 1.3.4)
-    tilt (1.4.1)
-    trollop (2.0)
+      tilt (>= 1.3, < 3)
+    streamio-ffmpeg (2.1.0)
+      multi_json (~> 1.8)
+    tilt (2.0.5)
+    trollop (2.1.2)
     vegas (0.1.11)
       rack (>= 1.0.0)
 
@@ -44,13 +48,15 @@ DEPENDENCIES
   builder
   curb
   fastimage
-  mime-types
+  mime-types (= 2.6.2)
   nokogiri
   open4
   redis
   resque
   rubyzip
+  streamio-ffmpeg
   trollop
 
 BUNDLED WITH
-   1.10.6
+   1.12.5
+
diff --git a/record-and-playback/core/lib/recordandplayback/edl/audio.rb b/record-and-playback/core/lib/recordandplayback/edl/audio.rb
index 943200b6b823fb866af1cb7e6509489560db8f6d..316a7d07954555b58af2751637baa7d4545a6a89 100644
--- a/record-and-playback/core/lib/recordandplayback/edl/audio.rb
+++ b/record-and-playback/core/lib/recordandplayback/edl/audio.rb
@@ -172,15 +172,22 @@ module BigBlueButton
 
       def self.audio_info(filename)
         IO.popen([*FFPROBE, filename]) do |probe|
-          info = JSON.parse(probe.read, :symbolize_names => true)
-          if !info[:streams]
-            return {}
+          info = nil
+          begin
+            info = JSON.parse(probe.read, :symbolize_names => true)
+          rescue StandardError => e
+            BigBlueButton.logger.warn("Couldn't parse audio info: #{e}")
           end
-          info[:audio] = info[:streams].find { |stream| stream[:codec_type] == 'audio' }
+          return {} if !info
+          return {} if !info[:streams]
+          return {} if !info[:format]
 
-          if info[:audio]
-            info[:sample_rate] = info[:audio][:sample_rate].to_i
+          info[:audio] = info[:streams].find do |stream|
+            stream[:codec_type] == 'audio'
           end
+          return {} if !info[:audio]
+
+          info[:sample_rate] = info[:audio][:sample_rate].to_i
 
           if info[:format][:format_name] == 'wav'
             # wav files generated by freeswitch can have incorrect length
diff --git a/record-and-playback/core/lib/recordandplayback/edl/video.rb b/record-and-playback/core/lib/recordandplayback/edl/video.rb
index 2e5d1429829eb22db9349382a3fa552a3837cd0e..c5e4084928f084531b7146af8a6365fd30945939 100644
--- a/record-and-playback/core/lib/recordandplayback/edl/video.rb
+++ b/record-and-playback/core/lib/recordandplayback/edl/video.rb
@@ -269,33 +269,35 @@ module BigBlueButton
 
       def self.video_info(filename)
         IO.popen([*FFPROBE, filename]) do |probe|
-          info = JSON.parse(probe.read, :symbolize_names => true)
+          info = nil
+          begin
+            info = JSON.parse(probe.read, :symbolize_names => true)
+          rescue StandardError => e
+            BigBlueButton.logger.warn("Couldn't parse video info: #{e}")
+          end
           return {} if !info
+          return {} if !info[:streams]
+          return {} if !info[:format]
 
-          if info[:streams]
-            info[:video] = info[:streams].find { |stream| stream[:codec_type] == 'video' }
-            info[:audio] = info[:streams].find { |stream| stream[:codec_type] == 'audio' }
+          info[:video] = info[:streams].find do |stream|
+            stream[:codec_type] == 'audio'
           end
 
-          if info[:video]
-            info[:width] = info[:video][:width].to_i
-            info[:height] = info[:video][:height].to_i
+          return {} if !info[:video]
 
-            return {} if info[:width] == 0 or info[:height] == 0
-            return {} if info[:video][:display_aspect_ratio] == '0:0'
+          info[:width] = info[:video][:width].to_i
+          info[:height] = info[:video][:height].to_i
 
-            info[:aspect_ratio] = info[:video][:display_aspect_ratio].to_r
-            if info[:aspect_ratio] == 0
-              info[:aspect_ratio] = Rational(info[:width], info[:height])
-            end
+          return {} if info[:width] == 0 or info[:height] == 0
+          return {} if info[:video][:display_aspect_ratio] == '0:0'
+
+          info[:aspect_ratio] = Rational(*(info[:video][:display_aspect_ratio].split(':')))
+          if info[:aspect_ratio] == 0
+            info[:aspect_ratio] = Rational(info[:width], info[:height])
           end
 
           # Convert the duration to milliseconds
-          if info[:format]
-            info[:duration] = (info[:format][:duration].to_r * 1000).to_i
-          else
-            info[:duration] = 0
-          end
+          info[:duration] = (info[:format][:duration].to_r * 1000).to_i
 
           return info
         end