diff --git a/akka-bbb-apps/build.sbt b/akka-bbb-apps/build.sbt
index 84bd7c904c9cc36da0a34d69b94e700df56fac17..5954a0627f8086ecec630d0775b2924b4d5eab2c 100755
--- a/akka-bbb-apps/build.sbt
+++ b/akka-bbb-apps/build.sbt
@@ -55,7 +55,7 @@ libraryDependencies ++= {
     "org.pegdown"              %   "pegdown"                              % "1.4.0",
     "junit"                    %   "junit"                                % "4.11",
     "com.etaty.rediscala"      %%  "rediscala"                            % "1.4.0",
-    "commons-codec"             %  "commons-codec"                        % "1.8",
+    "commons-codec"             %  "commons-codec"                        % "1.10",
     "joda-time"                 %  "joda-time"                            % "2.3",
     "com.google.code.gson"      %  "gson"                                 % "2.5",
     "redis.clients"             %  "jedis"                                % "2.7.2",
diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java
index 1c586454e57a50eded00f495ef5337257e10b48f..cf1100af5bd4f1133033b3df8adfe11fcf800e0c 100755
--- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java
+++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/MeetingMessageReceiver.java
@@ -35,7 +35,7 @@ public class MeetingMessageReceiver implements MessageHandler {
 	
 	public void handleMessage(String pattern, String channel, String message) {
 		if (channel.equalsIgnoreCase(MessagingConstants.TO_MEETING_CHANNEL)) {
-			System.out.println("Meeting message: " + channel + " " + message);
+		    System.out.println("Meeting message: " + channel + " " + message);
 
 			JsonParser parser = new JsonParser();
 			JsonObject obj = (JsonObject) parser.parse(message);
@@ -45,8 +45,8 @@ public class MeetingMessageReceiver implements MessageHandler {
 					String messageName = header.get("name").getAsString();
 					if (CreateMeetingRequest.NAME.equals(messageName)) {
 						Gson gson = new Gson();
-						CreateMeetingRequest msg = gson.fromJson(message,
-								CreateMeetingRequest.class);
+                        CreateMeetingRequest msg = gson.fromJson(message,
+                                CreateMeetingRequest.class);
 						bbbGW.handleBigBlueButtonMessage(msg);
 					}
 				}
@@ -59,49 +59,50 @@ public class MeetingMessageReceiver implements MessageHandler {
 					EndMeetingMessage emm = (EndMeetingMessage) msg;
 					bbbGW.endMeeting(emm.meetingId);
 				} else if (msg instanceof RegisterUserMessage) {
-					RegisterUserMessage emm = (RegisterUserMessage) msg;
-					bbbGW.registerUser(emm.meetingID, emm.internalUserId, emm.fullname, emm.role, emm.externUserID, emm.authToken, emm.avatarURL);
+					RegisterUserMessage rum = (RegisterUserMessage) msg;
+					bbbGW.registerUser(rum.meetingID, rum.internalUserId, rum.fullname, rum.role, rum.externUserID, rum.authToken, rum.avatarURL);
 				} else if (msg instanceof DestroyMeetingMessage) {
-					DestroyMeetingMessage emm = (DestroyMeetingMessage) msg;
-					bbbGW.destroyMeeting(emm.meetingId);
+					DestroyMeetingMessage dmm = (DestroyMeetingMessage) msg;
+					bbbGW.destroyMeeting(dmm.meetingId);
 				} else if (msg instanceof ValidateAuthTokenMessage) {
-					ValidateAuthTokenMessage emm = (ValidateAuthTokenMessage) msg;
+					ValidateAuthTokenMessage vam = (ValidateAuthTokenMessage) msg;
 					String sessionId = "tobeimplemented";
-					bbbGW.validateAuthToken(emm.meetingId, emm.userId, emm.token, emm.replyTo, sessionId);
+					bbbGW.validateAuthToken(vam.meetingId, vam.userId, vam.token, vam.replyTo, sessionId);
 				} else if (msg instanceof UserConnectedToGlobalAudio) {
-					UserConnectedToGlobalAudio emm = (UserConnectedToGlobalAudio) msg;
+					UserConnectedToGlobalAudio ustga = (UserConnectedToGlobalAudio) msg;
 					
 					Map<String, Object> logData = new HashMap<String, Object>();
-					logData.put("voiceConf", emm.voiceConf);
-					logData.put("userId", emm.userid);
-					logData.put("username", emm.name);
+					logData.put("voiceConf", ustga.voiceConf);
+					logData.put("userId", ustga.userid);
+					logData.put("username", ustga.name);
 					logData.put("event", "user_connected_to_global_audio");
 					logData.put("description", "User connected to global audio.");
 					
+					/*
 					Gson gson = new Gson();
 					String logStr =  gson.toJson(logData);
-					
-					//System.out.println("User connected to global audio: data={}", logStr);
-					
-					bbbGW.userConnectedToGlobalAudio(emm.voiceConf, emm.userid, emm.name);
+					System.out.println("User connected to global audio: data={}", logStr);
+					 */
+					bbbGW.userConnectedToGlobalAudio(ustga.voiceConf, ustga.userid, ustga.name);
 				} else if (msg instanceof UserDisconnectedFromGlobalAudio) {
-					UserDisconnectedFromGlobalAudio emm = (UserDisconnectedFromGlobalAudio) msg;
+					UserDisconnectedFromGlobalAudio udfga = (UserDisconnectedFromGlobalAudio) msg;
 					
 					Map<String, Object> logData = new HashMap<String, Object>();
-					logData.put("voiceConf", emm.voiceConf);
-					logData.put("userId", emm.userid);
-					logData.put("username", emm.name);
+					logData.put("voiceConf", udfga.voiceConf);
+					logData.put("userId", udfga.userid);
+					logData.put("username", udfga.name);
 					logData.put("event", "user_disconnected_from_global_audio");
 					logData.put("description", "User disconnected from global audio.");
 					
+					/*
 					Gson gson = new Gson();
 					String logStr =  gson.toJson(logData);
-					
-					//System.out.println("User disconnected from global audio: data={}", logStr);
-					bbbGW.userDisconnectedFromGlobalAudio(emm.voiceConf, emm.userid, emm.name);
+					System.out.println("User disconnected from global audio: data={}", logStr);
+					*/
+					bbbGW.userDisconnectedFromGlobalAudio(udfga.voiceConf, udfga.userid, udfga.name);
 				}
 				else if (msg instanceof GetAllMeetingsRequest) {
-					GetAllMeetingsRequest emm = (GetAllMeetingsRequest) msg;
+					GetAllMeetingsRequest gamr = (GetAllMeetingsRequest) msg;
 					bbbGW.getAllMeetings("no_need_of_a_meeting_id");
 				} else {
 					System.out.println("Unknown message: [" + message + "]");
@@ -126,8 +127,8 @@ public class MeetingMessageReceiver implements MessageHandler {
 						
 						if (msg != null) {
 							if (msg instanceof KeepAliveMessage) {
-								KeepAliveMessage emm = (KeepAliveMessage) msg;
-								bbbGW.isAliveAudit(emm.keepAliveId);					
+								KeepAliveMessage kam = (KeepAliveMessage) msg;
+								bbbGW.isAliveAudit(kam.keepAliveId);					
 							}
 						} else {
 							System.out.println("Unknown message: [" + message + "]");
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
index 9fd760f88e270994dae062df04fadfe1e7892dd2..a2e84486c09eb7b039caf2722f915b389a0bbadf 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
@@ -11,13 +11,13 @@ import org.bigbluebutton.SystemConfiguration
 
 object BigBlueButtonActor extends SystemConfiguration {
   def props(system: ActorSystem,
-    eventBus: IncomingEventBus,
-    outGW: OutMessageGateway): Props =
+            eventBus: IncomingEventBus,
+            outGW: OutMessageGateway): Props =
     Props(classOf[BigBlueButtonActor], system, eventBus, outGW)
 }
 
 class BigBlueButtonActor(val system: ActorSystem,
-    eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
+                         eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
 
   implicit def executionContext = system.dispatcher
   implicit val timeout = Timeout(5 seconds)
@@ -25,19 +25,19 @@ class BigBlueButtonActor(val system: ActorSystem,
   private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
 
   def receive = {
-    case msg: CreateMeeting => handleCreateMeeting(msg)
-    case msg: DestroyMeeting => handleDestroyMeeting(msg)
-    case msg: KeepAliveMessage => handleKeepAliveMessage(msg)
-    case msg: PubSubPing => handlePubSubPingMessage(msg)
-    case msg: ValidateAuthToken => handleValidateAuthToken(msg)
-    case msg: GetAllMeetingsRequest => handleGetAllMeetingsRequest(msg)
-    case msg: UserJoinedVoiceConfMessage => handleUserJoinedVoiceConfMessage(msg)
-    case msg: UserLeftVoiceConfMessage => handleUserLeftVoiceConfMessage(msg)
-    case msg: UserLockedInVoiceConfMessage => handleUserLockedInVoiceConfMessage(msg)
-    case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
-    case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg)
+    case msg: CreateMeeting                    => handleCreateMeeting(msg)
+    case msg: DestroyMeeting                   => handleDestroyMeeting(msg)
+    case msg: KeepAliveMessage                 => handleKeepAliveMessage(msg)
+    case msg: PubSubPing                       => handlePubSubPingMessage(msg)
+    case msg: ValidateAuthToken                => handleValidateAuthToken(msg)
+    case msg: GetAllMeetingsRequest            => handleGetAllMeetingsRequest(msg)
+    case msg: UserJoinedVoiceConfMessage       => handleUserJoinedVoiceConfMessage(msg)
+    case msg: UserLeftVoiceConfMessage         => handleUserLeftVoiceConfMessage(msg)
+    case msg: UserLockedInVoiceConfMessage     => handleUserLockedInVoiceConfMessage(msg)
+    case msg: UserMutedInVoiceConfMessage      => handleUserMutedInVoiceConfMessage(msg)
+    case msg: UserTalkingInVoiceConfMessage    => handleUserTalkingInVoiceConfMessage(msg)
     case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(msg)
-    case _ => // do nothing
+    case _                                     => // do nothing
   }
 
   private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
@@ -117,9 +117,9 @@ class BigBlueButtonActor(val system: ActorSystem,
         meetings -= msg.meetingID
         log.info("Kick everyone out on meetingId={}", msg.meetingID)
         if (m.mProps.isBreakout) {
-          log.info("Informing parent meeting {} that a breakout room has been ended {}", m.mProps.externalMeetingID, m.mProps.meetingID)
-          eventBus.publish(BigBlueButtonEvent(m.mProps.externalMeetingID,
-            BreakoutRoomEnded(m.mProps.externalMeetingID, m.mProps.meetingID)))
+          log.info("Informing parent meeting {} that a breakout room has been ended {}", m.mProps.parentMeetingID, m.mProps.meetingID)
+          eventBus.publish(BigBlueButtonEvent(m.mProps.parentMeetingID,
+            BreakoutRoomEnded(m.mProps.parentMeetingID, m.mProps.meetingID)))
         }
         outGW.send(new EndAndKickAll(msg.meetingID, m.mProps.recorded))
         outGW.send(new DisconnectAllUsers(msg.meetingID))
@@ -149,9 +149,9 @@ class BigBlueButtonActor(val system: ActorSystem,
         eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
 
         meetings += m.mProps.meetingID -> m
-        outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName,
-          m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass,
-          msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate))
+        outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.parentMeetingID,
+          m.mProps.recorded, m.mProps.meetingName, m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass,
+          msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate, msg.mProps.isBreakout))
 
         m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded)
       }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
index 2b83e37d440b1daa3a078d0b97daeb1f6a74c383..7989bf5c84dc8ddb22197f6d6cc0852fade7ce81 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
@@ -57,6 +57,7 @@ class BigBlueButtonInGW(
         val mProps = new MeetingProperties(
           msg.payload.id,
           msg.payload.externalId,
+          msg.payload.parentId,
           msg.payload.name,
           msg.payload.record,
           msg.payload.voiceConfId,
@@ -69,7 +70,8 @@ class BigBlueButtonInGW(
           msg.payload.createTime,
           msg.payload.createDate,
           red5DeskShareIP, red5DeskShareApp,
-          msg.payload.isBreakout)
+          msg.payload.isBreakout,
+          msg.payload.sequence)
 
         eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps)))
       }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala
index cc2118cd0099bb6ccf6e434b33e7eff8e2f51e98..67d50713dde15387588e3c386b761c4723d2dfb0 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala
@@ -51,7 +51,7 @@ object JsonMessageDecoder {
   def decode(json: String): Option[InMessage] = {
     unmarshall(json) match {
       case Success(validMsg) => Some(validMsg)
-      case Failure(ex) => None
+      case Failure(ex)       => None
     }
   }
 
@@ -75,4 +75,4 @@ object JsonMessageDecoder {
       case _ => throw MessageProcessException("Cannot parse JSON message: [" + msg + "]")
     }
   }
-}
\ No newline at end of file
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala
index 9ec02759c9a10fceeccab00f176d1e270e1cb18d..17d133ab66883beefa6d8e7a3a15cfa923619d8f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala
@@ -38,27 +38,27 @@ class JsonMessageSenderActor(val service: MessageSender)
   def receive = {
 
     // Breakout
-    case msg: CreateBreakoutRoom => handleCreateBreakoutRoom(msg)
-    case msg: EndBreakoutRoom => handleEndBreakoutRoom(msg)
-    case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsList(msg)
+    case msg: CreateBreakoutRoom            => handleCreateBreakoutRoom(msg)
+    case msg: EndBreakoutRoom               => handleEndBreakoutRoom(msg)
+    case msg: BreakoutRoomsListOutMessage   => handleBreakoutRoomsList(msg)
     case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURL(msg)
     case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStarted(msg)
-    case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEnded(msg)
+    case msg: BreakoutRoomEndedOutMessage   => handleBreakoutRoomEnded(msg)
     case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsers(msg)
-    case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg)
+    case msg: MeetingTimeRemainingUpdate    => handleMeetingTimeRemainingUpdate(msg)
 
-    case _ => // do nothing
+    case _                                  => // do nothing
   }
 
   // Breakout
   private def handleBreakoutRoomStarted(msg: BreakoutRoomStartedOutMessage) {
-    val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakout.breakoutId, msg.breakout.name)
+    val payload = new BreakoutRoomPayload(msg.parentMeetingId, msg.breakout.meetingId, msg.breakout.externalMeetingId, msg.breakout.name, msg.breakout.sequence)
     val request = new BreakoutRoomStarted(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
   }
 
   private def handleBreakoutRoomEnded(msg: BreakoutRoomEndedOutMessage) {
-    val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakoutId, "")
+    val payload = new BreakoutRoomPayload(msg.parentMeetingId, msg.meetingId, "", "", 0)
     val request = new BreakoutRoomClosed(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
   }
@@ -66,7 +66,7 @@ class JsonMessageSenderActor(val service: MessageSender)
   private def handleUpdateBreakoutUsers(msg: UpdateBreakoutUsersOutMessage) {
     val users = new java.util.ArrayList[BreakoutUserPayload]()
     msg.users.foreach(x => users.add(new BreakoutUserPayload(x.id, x.name)))
-    val payload = new UpdateBreakoutUsersPayload(msg.meetingId, msg.breakoutId, users)
+    val payload = new UpdateBreakoutUsersPayload(msg.parentMeetingId, msg.breakoutMeetingId, users)
     val request = new UpdateBreakoutUsers(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
   }
@@ -85,29 +85,29 @@ class JsonMessageSenderActor(val service: MessageSender)
 
   private def handleBreakoutRoomsList(msg: BreakoutRoomsListOutMessage) {
     val rooms = new java.util.ArrayList[BreakoutRoomPayload]()
-    msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.breakoutId, r.name)))
+    msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.meetingId, r.externalMeetingId, r.name, r.sequence)))
     val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms, msg.roomsReady)
     val request = new BreakoutRoomsList(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
   }
 
   private def handleCreateBreakoutRoom(msg: CreateBreakoutRoom) {
-    val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutId, msg.room.parentId, msg.room.name,
-      msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword,
+    val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutMeetingId, msg.room.parentId, msg.room.name,
+      msg.room.sequence, msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword,
       msg.room.durationInMinutes, msg.room.sourcePresentationId, msg.room.sourcePresentationSlide, msg.room.record)
     val request = new CreateBreakoutRoomRequest(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
   }
 
   private def handleEndBreakoutRoom(msg: EndBreakoutRoom) {
-    val payload = new EndBreakoutRoomRequestPayload(msg.breakoutId)
+    val payload = new EndBreakoutRoomRequestPayload(msg.breakoutMeetingId)
     val request = new EndBreakoutRoomRequest(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
   }
 
   def handleBreakoutRoomJoinURL(msg: BreakoutRoomJoinURLOutMessage) {
-    val payload = new BreakoutRoomJoinURLPayload(msg.meetingId,
-      msg.breakoutId, msg.userId, msg.joinURL)
+    val payload = new BreakoutRoomJoinURLPayload(msg.parentMeetingId,
+      msg.breakoutMeetingId, msg.userId, msg.joinURL)
     val request = new BreakoutRoomJoinURL(payload)
     service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
   }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
index 9514ab22033f8544433e3378d9aa8a03f0455880..63c9f46a6cf3fee23600a7a321bd969f26652c5f 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
@@ -12,17 +12,17 @@ import org.bigbluebutton.core.apps.CaptionApp
 import org.bigbluebutton.core.apps.CaptionModel
 
 class LiveMeeting(val mProps: MeetingProperties,
-  val eventBus: IncomingEventBus,
-  val outGW: OutMessageGateway,
-  val chatModel: ChatModel,
-  val layoutModel: LayoutModel,
-  val meetingModel: MeetingModel,
-  val usersModel: UsersModel,
-  val pollModel: PollModel,
-  val wbModel: WhiteboardModel,
-  val presModel: PresentationModel,
-  val breakoutModel: BreakoutRoomModel,
-  val captionModel: CaptionModel)(implicit val context: ActorContext)
+                  val eventBus: IncomingEventBus,
+                  val outGW: OutMessageGateway,
+                  val chatModel: ChatModel,
+                  val layoutModel: LayoutModel,
+                  val meetingModel: MeetingModel,
+                  val usersModel: UsersModel,
+                  val pollModel: PollModel,
+                  val wbModel: WhiteboardModel,
+                  val presModel: PresentationModel,
+                  val breakoutModel: BreakoutRoomModel,
+                  val captionModel: CaptionModel)(implicit val context: ActorContext)
     extends UsersApp with PresentationApp
     with LayoutApp with ChatApp with WhiteboardApp with PollApp
     with BreakoutRoomApp with CaptionApp {
@@ -56,7 +56,7 @@ class LiveMeeting(val mProps: MeetingProperties,
   }
 
   def startCheckingIfWeNeedToEndVoiceConf() {
-    if (usersModel.numWebUsers == 0) {
+    if (usersModel.numWebUsers == 0 && !mProps.isBreakout) {
       meetingModel.lastWebUserLeft()
       log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
     }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
index c3a67357ddf95c55d19cc5f7dc4fa4bcf0bdc683..5c8e3dd18e7c7cde37b072440e1f91217b2098a1 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
@@ -37,8 +37,8 @@ class MeetingActorInternal(val mProps: MeetingProperties,
   if (mProps.isBreakout) {
     // This is a breakout room. Inform our parent meeting that we have been successfully created.
     eventBus.publish(BigBlueButtonEvent(
-      mProps.externalMeetingID,
-      BreakoutRoomCreated(mProps.externalMeetingID, mProps.meetingID)))
+      mProps.parentMeetingID,
+      BreakoutRoomCreated(mProps.parentMeetingID, mProps.meetingID)))
   }
 
   def receive = {
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
index 241bde0f856baf53663f9f00187ccadfa4a319dc..81509b3aa2d52b34d6e4ba35b67efc4e9b9d9ce1 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
@@ -4,14 +4,15 @@ import org.bigbluebutton.core.api.Permissions
 import java.util.concurrent.TimeUnit
 
 case object StopMeetingActor
-case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
-  voiceBridge: String, deskshareBridge: String, duration: Int, autoStartRecording: Boolean,
-  allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String, createTime: Long,
-  createDate: String, red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
+case class MeetingProperties(meetingID: String, externalMeetingID: String, parentMeetingID: String, meetingName: String,
+                             recorded: Boolean, voiceBridge: String, deskshareBridge: String, duration: Int,
+                             autoStartRecording: Boolean, allowStartStopRecording: Boolean, moderatorPass: String,
+                             viewerPass: String, createTime: Long, createDate: String,
+                             red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean, sequence: Int)
 
 case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
-  sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
-  sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false)
+                                sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
+                                sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false)
 
 class MeetingModel {
   private var audioSettingsInited = false
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala
index e587c3c24011d4b15c3bd9fefec524eb6627766f..391f888c22911abc84e65d5852d4d5fb6319a091 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MessageSenderActor.scala
@@ -44,7 +44,6 @@ class MessageSenderActor(val service: MessageSender)
     extends Actor with ActorLogging {
 
   val encoder = new ToJsonEncoder()
-
   def receive = {
     case msg: UserEjectedFromMeeting => handleUserEjectedFromMeeting(msg)
     case msg: GetChatHistoryReply => handleGetChatHistoryReply(msg)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala
index 2f74e5b5270dd4ac875fc0ed511569187b8018bf..7138d1eb811e579da1e70b3a5a0bce13e8752672 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala
@@ -4,7 +4,7 @@ import spray.json.{ DefaultJsonProtocol, JsValue, JsString, DeserializationExcep
 import org.bigbluebutton.core.api._
 
 object UserMessagesProtocol extends DefaultJsonProtocol {
-  /*  
+  /*
   implicit object RoleJsonFormat extends JsonFormat[Role.RoleType] {
   	def write(obj: Role.RoleType): JsValue = JsString(obj.toString)
   	
@@ -20,21 +20,20 @@ object UserMessagesProtocol extends DefaultJsonProtocol {
 
     def read(json: JsValue): MessageType.MessageType = json match {
       case JsString(str) => MessageType.withName(str)
-      case _ => throw new DeserializationException("Enum string expected")
+      case _             => throw new DeserializationException("Enum string expected")
     }
   }
 
-  implicit val breakoutRoomInPayloadFormat = jsonFormat2(BreakoutRoomInPayload)
-  implicit val createBreakoutRoomsFormat = jsonFormat4(CreateBreakoutRooms)
+  implicit val breakoutRoomInPayloadFormat = jsonFormat3(BreakoutRoomInPayload)
+  implicit val createBreakoutRoomsFormat = jsonFormat5(CreateBreakoutRooms)
   implicit val breakoutRoomsListMessageFormat = jsonFormat1(BreakoutRoomsListMessage)
-  implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat3(RequestBreakoutJoinURLInMessage)
+  implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat4(RequestBreakoutJoinURLInMessage)
   implicit val transferUserToMeetingRequestFormat = jsonFormat3(TransferUserToMeetingRequest)
   implicit val endBreakoutRoomsFormat = jsonFormat1(EndAllBreakoutRooms)
   implicit val inMsgHeaderFormat = jsonFormat1(InMessageHeader)
   implicit val outMsgHeaderFormat = jsonFormat1(OutMsgHeader)
   implicit val outMsgEnvelopeHeaderFormat = jsonFormat2(OutMsgEnvelopeHeader)
-  implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat10(CreateBreakoutRoomOutMsgPayload)
+  implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat11(CreateBreakoutRoomOutMsgPayload)
   implicit val createBreakoutRoomOutMsgEnvelopePayloadFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelopePayload)
   implicit val createBreakoutRoomOutMsgEnvelopeFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelope)
-
 }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala
index 35e0601bc90ddcd6e5a2016dcc3ecfc136509b48..327c8af5216c84f3460c836f5f1d1527845628f9 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala
@@ -7,8 +7,7 @@ trait OutMessage
 
 case class CreateBreakoutRoomOutMsgEnvelope(header: OutMsgEnvelopeHeader, payload: CreateBreakoutRoomOutMsgEnvelopePayload)
 case class CreateBreakoutRoomOutMsgEnvelopePayload(header: OutMsgHeader, payload: CreateBreakoutRoomOutMsgPayload)
-case class CreateBreakoutRoomOutMsgPayload(breakoutId: String, name: String, parentId: String,
-  voiceConfId: String, durationInMinutes: Int,
-  moderatorPassword: String, viewerPassword: String,
-  sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean)
-
+case class CreateBreakoutRoomOutMsgPayload(meetingId: String, parentId: String, name: String,
+                                           voiceConfId: String, moderatorPassword: String, viewerPassword: String,
+                                           durationInMinutes: Int, sourcePresentationId: String, sourcePresentationSlide: Int,
+                                           record: Boolean, sequence: Int)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala
index 6ca5af36db6ce5d2cdf79303767852e932a0b2fb..c55149c0dc41ca529eba0662c132ac189eb93905 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/Constants.scala
@@ -6,6 +6,8 @@ object Constants {
   val PAYLOAD = "payload"
   val MEETING_ID = "meeting_id"
   val EXTERNAL_MEETING_ID = "external_meeting_id"
+  val PARENT_MEETING_ID = "parent_meeting_id"
+  val IS_BREAKOUT = "is_breakout";
   val TIMESTAMP = "timestamp"
   val CURRENT_TIME = "current_time"
   val USER_ID = "userid"
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala
index 87f553a2ec8d8a36cdc676cf9f0216111979ef27..805e890ac5142d306dca4e314c8cae184553f6ed 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala
@@ -42,17 +42,14 @@ case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String,
 // Sent by user to request the breakout rooms list of a room
 case class BreakoutRoomsListMessage(meetingId: String) extends InMessage
 // Sent by user to request creation of breakout rooms
-case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, record: Boolean,
-  rooms: Vector[BreakoutRoomInPayload]) extends InMessage
-case class BreakoutRoomInPayload(name: String, users: Vector[String])
+case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, record: Boolean, redirectOnJoin: Boolean, rooms: Vector[BreakoutRoomInPayload]) extends InMessage
+case class BreakoutRoomInPayload(name: String, sequence: Int, users: Vector[String])
 // Sent by user to request for a join URL in order to be able to join a breakout room
-case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutId: String,
-  userId: String) extends InMessage
+case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutMeetingId: String, userId: String, redirect: Boolean) extends InMessage
 // Sent by breakout actor to tell meeting actor that breakout room has been created.
 case class BreakoutRoomCreated(meetingId: String, breakoutRoomId: String) extends InMessage
 // Sent by breakout actor to tell meeting actor the list of users in the breakout room.    
-case class BreakoutRoomUsersUpdate(meetingId: String, breakoutId: String,
-  users: Vector[BreakoutUser]) extends InMessage
+case class BreakoutRoomUsersUpdate(meetingId: String, breakoutMeetingId: String, users: Vector[BreakoutUser]) extends InMessage
 // Send by internal actor to tell the breakout actor to send it's list of users to the main meeting actor.    
 case class SendBreakoutUsersUpdate(meetingId: String) extends InMessage
 // Sent by user to request ending all the breakout rooms
@@ -76,9 +73,9 @@ case class GetLockSettings(meetingID: String, userId: String) extends InMessage
 /////////////////////////////////////////////////////////////////////////////////
 
 case class ValidateAuthToken(meetingID: String, userId: String, token: String,
-  correlationId: String, sessionId: String) extends InMessage
+                             correlationId: String, sessionId: String) extends InMessage
 case class RegisterUser(meetingID: String, userID: String, name: String, role: Role,
-  extUserID: String, authToken: String, avatarURL: String) extends InMessage
+                        extUserID: String, authToken: String, avatarURL: String) extends InMessage
 case class UserJoining(meetingID: String, userID: String, authToken: String) extends InMessage
 case class UserLeaving(meetingID: String, userID: String, sessionId: String) extends InMessage
 case class GetUsers(meetingID: String, requesterID: String) extends InMessage
@@ -100,9 +97,9 @@ case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo
 case class SendPublicMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
 case class SendPrivateMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
 case class UserConnectedToGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String,
-  userid: String, name: String) extends InMessage
+                                      userid: String, name: String) extends InMessage
 case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String,
-  userid: String, name: String) extends InMessage
+                                           userid: String, name: String) extends InMessage
 
 ///////////////////////////////////////////////////////////////////////////////////////
 // Layout
@@ -111,7 +108,7 @@ case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just
 case class GetCurrentLayoutRequest(meetingID: String, requesterID: String) extends InMessage
 case class SetLayoutRequest(meetingID: String, requesterID: String, layoutID: String) extends InMessage
 case class LockLayoutRequest(meetingID: String, setById: String, lock: Boolean, viewersOnly: Boolean,
-  layout: Option[String]) extends InMessage
+                             layout: Option[String]) extends InMessage
 case class BroadcastLayoutRequest(meetingID: String, requesterID: String, layout: String) extends InMessage
 
 //////////////////////////////////////////////////////////////////////////////////////
@@ -123,19 +120,19 @@ case class RemovePresentation(meetingID: String, presentationID: String) extends
 case class GetPresentationInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage
 case class SendCursorUpdate(meetingID: String, xPercent: Double, yPercent: Double) extends InMessage
 case class ResizeAndMoveSlide(meetingID: String, xOffset: Double, yOffset: Double,
-  widthRatio: Double, heightRatio: Double) extends InMessage
+                              widthRatio: Double, heightRatio: Double) extends InMessage
 case class GotoSlide(meetingID: String, page: String) extends InMessage
 case class SharePresentation(meetingID: String, presentationID: String, share: Boolean) extends InMessage
 case class GetSlideInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage
 case class PreuploadedPresentations(meetingID: String, presentations: Seq[Presentation]) extends InMessage
 case class PresentationConversionUpdate(meetingID: String, messageKey: String, code: String,
-  presentationId: String, presName: String) extends InMessage
+                                        presentationId: String, presName: String) extends InMessage
 case class PresentationPageCountError(meetingID: String, messageKey: String, code: String, presentationId: String,
-  numberOfPages: Int, maxNumberPages: Int, presName: String) extends InMessage
+                                      numberOfPages: Int, maxNumberPages: Int, presName: String) extends InMessage
 case class PresentationSlideGenerated(meetingID: String, messageKey: String, code: String, presentationId: String,
-  numberOfPages: Int, pagesCompleted: Int, presName: String) extends InMessage
+                                      numberOfPages: Int, pagesCompleted: Int, presName: String) extends InMessage
 case class PresentationConversionCompleted(meetingID: String, messageKey: String, code: String,
-  presentation: Presentation) extends InMessage
+                                           presentation: Presentation) extends InMessage
 
 /////////////////////////////////////////////////////////////////////////////////////  
 // Polling
@@ -164,9 +161,9 @@ case class MuteUserRequest(meetingID: String, requesterID: String, userID: Strin
 case class LockUserRequest(meetingID: String, requesterID: String, userID: String, lock: Boolean) extends InMessage
 case class EjectUserFromVoiceRequest(meetingID: String, userId: String, ejectedBy: String) extends InMessage
 case class VoiceUserJoinedMessage(meetingID: String, user: String, voiceConfId: String,
-  callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage
+                                  callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage
 case class UserJoinedVoiceConfMessage(voiceConfId: String, voiceUserId: String, userId: String, externUserId: String,
-  callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage
+                                      callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage
 case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String) extends InMessage
 case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean) extends InMessage
 case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean) extends InMessage
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala
index a356e8454784ec92236a7448834d7d2899bf5680..5c0753e492e638736d9f7c7ed05b4b8d9ccb8120 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/OutMessages.scala
@@ -14,8 +14,8 @@ case class VoiceRecordingStarted(meetingID: String, recorded: Boolean, recording
 case class VoiceRecordingStopped(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage
 case class RecordingStatusChanged(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
 case class GetRecordingStatusReply(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
-case class MeetingCreated(meetingID: String, externalMeetingID: String, recorded: Boolean, name: String,
-  voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage
+case class MeetingCreated(meetingID: String, externalMeetingID: String, parentMeetingID: String, recorded: Boolean, name: String,
+                          voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, isBreakout: Boolean) extends IOutMessage
 case class MeetingMuted(meetingID: String, recorded: Boolean, meetingMuted: Boolean) extends IOutMessage
 case class MeetingEnded(meetingID: String, recorded: Boolean, voiceBridge: String) extends IOutMessage
 case class MeetingState(meetingID: String, recorded: Boolean, userId: String, permissions: Permissions, meetingMuted: Boolean) extends IOutMessage
@@ -30,17 +30,17 @@ case object IsAliveMessage extends IOutMessage
 // Breakout Rooms
 case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody], roomsReady: Boolean) extends IOutMessage
 case class CreateBreakoutRoom(meetingId: String, room: BreakoutRoomOutPayload) extends IOutMessage
-case class EndBreakoutRoom(breakoutId: String) extends IOutMessage
-case class BreakoutRoomOutPayload(breakoutId: String, name: String, parentId: String,
+case class EndBreakoutRoom(breakoutMeetingId: String) extends IOutMessage
+case class BreakoutRoomOutPayload(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer,
   voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
   sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean)
-case class BreakoutRoomJoinURLOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, userId: String, joinURL: String) extends IOutMessage
-case class BreakoutRoomStartedOutMessage(meetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage
-case class BreakoutRoomBody(name: String, breakoutId: String)
-case class UpdateBreakoutUsersOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, users: Vector[BreakoutUser]) extends IOutMessage
+case class BreakoutRoomJoinURLOutMessage(parentMeetingId: String, recorded: Boolean, breakoutMeetingId: String, userId: String, joinURL: String) extends IOutMessage
+case class BreakoutRoomStartedOutMessage(parentMeetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage
+case class BreakoutRoomBody(name: String, externalMeetingId: String, meetingId: String, sequence: Int)
+case class UpdateBreakoutUsersOutMessage(parentMeetingId: String, recorded: Boolean, breakoutMeetingId: String, users: Vector[BreakoutUser]) extends IOutMessage
 case class MeetingTimeRemainingUpdate(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
 case class BreakoutRoomsTimeRemainingUpdateOutMessage(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
-case class BreakoutRoomEndedOutMessage(meetingId: String, breakoutId: String) extends IOutMessage
+case class BreakoutRoomEndedOutMessage(parentMeetingId: String, meetingId: String) extends IOutMessage
 
 // Permissions
 case class PermissionsSettingInitialized(meetingID: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
index 90a1d236a4a1d1da7b00149f13a4406696a73ddb..2d858d45b9a6c41a938fa3a3313c02c77fc3b896 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
@@ -21,8 +21,10 @@ trait BreakoutRoomApp extends SystemConfiguration {
   val eventBus: IncomingEventBus
 
   def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
-    val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.id) }
-    outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0));
+    val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.externalMeetingId, r.id, r.sequence) }
+    val roomsReady = breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0
+    log.info("Sending breakout rooms list to {} with containing {} room(s)", mProps.meetingID, breakoutRooms.length)
+    outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, roomsReady))
   }
 
   def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
@@ -31,18 +33,26 @@ trait BreakoutRoomApp extends SystemConfiguration {
       log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}", breakoutModel.pendingRoomsNumber, mProps.meetingID)
       return
     }
+    if (breakoutModel.getNumberOfRooms() > 0) {
+      log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}", breakoutModel.getNumberOfRooms(), mProps.meetingID)
+      return
+    }
 
     var i = 0
-    val sourcePresentationId = presModel.getCurrentPresentation().get.id
-    val sourcePresentationSlide = presModel.getCurrentPage().get.num
+    // in very rare cases the presentation conversion generates an error, what should we do?
+    // those cases where default.pdf is deleted from the whiteboard
+    val sourcePresentationId = if (!presModel.getCurrentPresentation().isEmpty) presModel.getCurrentPresentation().get.id else "blank"
+    val sourcePresentationSlide = if (!presModel.getCurrentPage().isEmpty) presModel.getCurrentPage().get.num else 0
     breakoutModel.pendingRoomsNumber = msg.rooms.length;
+    breakoutModel.redirectOnJoin = msg.redirectOnJoin;
 
     for (room <- msg.rooms) {
       i += 1
-      val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mProps.meetingID, i)
+      val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mProps.meetingID, i)
       val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i)
-      val r = breakoutModel.createBreakoutRoom(breakoutMeetingId, room.name, voiceConfId, room.users)
-      val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID,
+      val r = breakoutModel.createBreakoutRoom(mProps.meetingID, breakoutMeetingId._1, breakoutMeetingId._2, room.name,
+        room.sequence, voiceConfId, room.users)
+      val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID, r.sequence,
         r.voiceConfId, msg.durationInMinutes, mProps.moderatorPass, mProps.viewerPass,
         sourcePresentationId, sourcePresentationSlide, msg.record)
       outGW.send(new CreateBreakoutRoom(mProps.meetingID, p))
@@ -51,36 +61,37 @@ trait BreakoutRoomApp extends SystemConfiguration {
     meetingModel.breakoutRoomsStartedOn = timeNowInSeconds;
   }
 
-  def sendJoinURL(userId: String, breakoutId: String) {
+  def sendJoinURL(userId: String, externalMeetingId: String, redirect: Boolean) {
+    log.debug("Sending breakout meeting {} Join URL for user: {}", externalMeetingId, userId);
     for {
       user <- usersModel.getUser(userId)
       apiCall = "join"
-      params = BreakoutRoomsUtil.joinParams(user.name, userId, true, breakoutId, mProps.moderatorPass, true)
+      params = BreakoutRoomsUtil.joinParams(user.name, userId, true, externalMeetingId, mProps.moderatorPass, redirect)
       baseString = BreakoutRoomsUtil.createBaseString(params)
       checksum = BreakoutRoomsUtil.calculateChecksum(apiCall, baseString, bbbWebSharedSecret)
       joinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, baseString, checksum)
-    } yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, breakoutId, userId, joinURL))
+    } yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, externalMeetingId, userId, joinURL))
   }
 
   def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) {
-    sendJoinURL(msg.userId, msg.breakoutId)
+    sendJoinURL(msg.userId, msg.breakoutMeetingId, msg.redirect)
   }
 
   def handleBreakoutRoomCreated(msg: BreakoutRoomCreated) {
     breakoutModel.pendingRoomsNumber -= 1
     val room = breakoutModel.getBreakoutRoom(msg.breakoutRoomId)
     room foreach { room =>
-      sendBreakoutRoomStarted(mProps.meetingID, room.name, room.id, room.voiceConfId)
+      sendBreakoutRoomStarted(room.parentRoomId, room.name, room.externalMeetingId, room.id, room.sequence, room.voiceConfId)
     }
 
-    // We avoid sending invitation
+    // We postpone sending invitation until all breakout rooms have been created
     if (breakoutModel.pendingRoomsNumber == 0) {
       log.info("All breakout rooms created for meetingId={}", mProps.meetingID)
       breakoutModel.getRooms().foreach { room =>
         breakoutModel.getAssignedUsers(room.id) foreach { users =>
           users.foreach { u =>
-            log.debug("Sending Join URL for users: {}", u);
-            sendJoinURL(u, room.id)
+            log.debug("Sending Join URL for users");
+            sendJoinURL(u, room.externalMeetingId, breakoutModel.redirectOnJoin)
           }
         }
       }
@@ -88,8 +99,9 @@ trait BreakoutRoomApp extends SystemConfiguration {
     }
   }
 
-  def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, breakoutId: String, voiceConfId: String) {
-    outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, breakoutId)))
+  def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String, breakoutMeetingId: String, sequence: Int, voiceConfId: String) {
+    log.info("Sending breakout room started {} for parent meeting {} ", breakoutMeetingId, meetingId);
+    outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, externalMeetingId, breakoutMeetingId, sequence)))
   }
 
   def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) {
@@ -98,16 +110,16 @@ trait BreakoutRoomApp extends SystemConfiguration {
   }
 
   def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) {
-    breakoutModel.updateBreakoutUsers(msg.breakoutId, msg.users) foreach { room =>
-      outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutId, room.users))
+    breakoutModel.updateBreakoutUsers(msg.breakoutMeetingId, msg.users) foreach { room =>
+      outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutMeetingId, room.users))
     }
   }
 
   def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) {
     val users = usersModel.getUsers().toVector
     val breakoutUsers = users map { u => new BreakoutUser(u.externUserID, u.name) }
-    eventBus.publish(BigBlueButtonEvent(mProps.externalMeetingID,
-      new BreakoutRoomUsersUpdate(mProps.externalMeetingID, mProps.meetingID, breakoutUsers)))
+    eventBus.publish(BigBlueButtonEvent(mProps.parentMeetingID,
+      new BreakoutRoomUsersUpdate(mProps.parentMeetingID, mProps.meetingID, breakoutUsers)))
   }
 
   def handleTransferUserToMeeting(msg: TransferUserToMeetingRequest) {
@@ -146,18 +158,18 @@ trait BreakoutRoomApp extends SystemConfiguration {
 }
 
 object BreakoutRoomsUtil {
-  def createMeetingId(id: String, index: Int): String = {
-    id.concat("-").concat(index.toString())
+  def createMeetingIds(id: String, index: Int): (String, String) = {
+    val timeStamp = System.currentTimeMillis()
+    val externalHash = DigestUtils.sha1Hex(id.concat("-").concat(timeStamp.toString()).concat("-").concat(index.toString()))
+    val externalId = externalHash.concat("-").concat(timeStamp.toString())
+    val internalId = DigestUtils.sha1Hex(externalId).concat("-").concat(timeStamp.toString())
+    (internalId, externalId)
   }
 
   def createVoiceConfId(id: String, index: Int): String = {
     id.concat(index.toString())
   }
 
-  def fromSWFtoPDF(swfURL: String): String = {
-    swfURL.replace("swf", "pdf")
-  }
-
   def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = {
     var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
     apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
@@ -174,13 +186,13 @@ object BreakoutRoomsUtil {
     checksum(apiCall.concat(baseString).concat(sharedSecret))
   }
 
-  def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutId: String,
+  def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutMeetingId: String,
                  password: String, redirect: Boolean): mutable.Map[String, String] = {
     val params = new collection.mutable.HashMap[String, String]
     params += "fullName" -> urlEncode(username)
-    params += "userID" -> urlEncode(userId + "-" + breakoutId.substring(breakoutId.lastIndexOf("-") + 1));
+    params += "userID" -> urlEncode(userId + "-" + breakoutMeetingId.substring(breakoutMeetingId.lastIndexOf("-") + 1));
     params += "isBreakout" -> urlEncode(isBreakout.toString())
-    params += "meetingID" -> urlEncode(breakoutId)
+    params += "meetingID" -> urlEncode(breakoutMeetingId)
     params += "password" -> urlEncode(password)
     params += "redirect" -> urlEncode(redirect.toString())
 
@@ -218,18 +230,4 @@ object BreakoutRoomsUtil {
   def urlEncode(s: String): String = {
     URLEncoder.encode(s, "UTF-8");
   }
-
-  //
-  //encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent
-  //
-  def encodeURIComponent(component: String): String = {
-    URLEncoder.encode(component, "UTF-8")
-      .replaceAll("\\%28", "(")
-      .replaceAll("\\%29", ")")
-      .replaceAll("\\+", "%20")
-      .replaceAll("\\%27", "'")
-      .replaceAll("\\%21", "!")
-      .replaceAll("\\%7E", "~")
-  }
-
 }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala
index 5ec8a3f6d90e739ac5085372cc640cb7cb92e3e3..dbc653480e564f1bd4bc64d596ede0800ef5d02c 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomModel.scala
@@ -1,16 +1,16 @@
 package org.bigbluebutton.core.apps
 
-import scala.collection.mutable.ArrayBuffer
-import scala.collection.immutable.HashMap
+import scala.Vector
 
 case class BreakoutUser(id: String, name: String)
-case class BreakoutRoom(id: String, name: String, voiceConfId: String,
-  assignedUsers: Vector[String], users: Vector[BreakoutUser])
+case class BreakoutRoom(id: String, externalMeetingId: String, name: String, parentRoomId: String, sequence: Integer, voiceConfId: String,
+                        assignedUsers: Vector[String], users: Vector[BreakoutUser])
 
 class BreakoutRoomModel {
   private var rooms = new collection.immutable.HashMap[String, BreakoutRoom]
 
   var pendingRoomsNumber: Integer = 0
+  var redirectOnJoin: Boolean = false
 
   def add(room: BreakoutRoom): BreakoutRoom = {
     rooms += room.id -> room
@@ -21,9 +21,9 @@ class BreakoutRoomModel {
     rooms -= id
   }
 
-  def createBreakoutRoom(id: String, name: String, voiceConfId: String,
-    assignedUsers: Vector[String]): BreakoutRoom = {
-    val room = new BreakoutRoom(id, name, voiceConfId, assignedUsers, Vector())
+  def createBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, voiceConfId: String,
+                         assignedUsers: Vector[String]): BreakoutRoom = {
+    val room = new BreakoutRoom(id, externalMeetingId, name, parentRoomId, sequence, voiceConfId, assignedUsers, Vector())
     add(room)
   }
 
@@ -35,18 +35,21 @@ class BreakoutRoomModel {
     rooms.values.toArray
   }
 
-  def getAssignedUsers(breakoutId: String): Option[Vector[String]] = {
+  def getNumberOfRooms(): Int = {
+    rooms.size
+  }
+
+  def getAssignedUsers(breakoutMeetingId: String): Option[Vector[String]] = {
     for {
-      room <- rooms.get(breakoutId)
+      room <- rooms.get(breakoutMeetingId)
     } yield room.assignedUsers
   }
 
-  def updateBreakoutUsers(breakoutId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = {
+  def updateBreakoutUsers(breakoutMeetingId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = {
     for {
-      room <- rooms.get(breakoutId)
+      room <- rooms.get(breakoutMeetingId)
       newroom = room.copy(users = users)
       room2 = add(newroom)
     } yield room2
   }
 }
-
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala
index c8999da668edf644ebfd2ac3b61a993cf5a6e1b1..74f9e05337e74ee3ddbe79b7d3b0b7244cda2ed8 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/MeetingMessageToJsonConverter.scala
@@ -29,6 +29,8 @@ object MeetingMessageToJsonConverter {
     val payload = new java.util.HashMap[String, Any]()
     payload.put(Constants.MEETING_ID, msg.meetingID)
     payload.put(Constants.EXTERNAL_MEETING_ID, msg.externalMeetingID)
+    payload.put(Constants.PARENT_MEETING_ID, msg.parentMeetingID)
+    payload.put(Constants.IS_BREAKOUT, msg.isBreakout)
     payload.put(Constants.NAME, msg.name)
     payload.put(Constants.RECORDED, msg.recorded)
     payload.put(Constants.VOICE_CONF, msg.voiceBridge)
@@ -145,8 +147,10 @@ object MeetingMessageToJsonConverter {
 
   def breakoutRoomStartedOutMessageToJson(msg: BreakoutRoomStartedOutMessage): String = {
     val payload = new java.util.HashMap[String, Any]()
-    payload.put("meetingId", msg.meetingId)
-    payload.put("breakoutId", msg.breakout.breakoutId)
+    payload.put("meetingId", msg.breakout.meetingId)
+    payload.put("externalMeetingId", msg.breakout.externalMeetingId)
+    payload.put("parentMeetingId", msg.parentMeetingId)
+    payload.put("sequence", msg.breakout.sequence)
     payload.put("name", msg.breakout.name)
 
     val header = Util.buildHeader(BreakoutRoomStarted.NAME, None)
@@ -155,8 +159,8 @@ object MeetingMessageToJsonConverter {
 
   def breakoutRoomEndedOutMessageToJson(msg: BreakoutRoomEndedOutMessage): String = {
     val payload = new java.util.HashMap[String, Any]()
+    payload.put("parentMeetingId", msg.parentMeetingId)
     payload.put("meetingId", msg.meetingId)
-    payload.put("breakoutId", msg.breakoutId)
 
     val header = Util.buildHeader(BreakoutRoomClosed.NAME, None)
     Util.buildJson(header, payload)
@@ -164,8 +168,8 @@ object MeetingMessageToJsonConverter {
 
   def breakoutRoomJoinURLOutMessageToJson(msg: BreakoutRoomJoinURLOutMessage): String = {
     val payload = new java.util.HashMap[String, Any]()
-    payload.put("meetingId", msg.meetingId)
-    payload.put("breakoutId", msg.breakoutId)
+    payload.put("parentMeetingId", msg.parentMeetingId)
+    payload.put("breakoutMeetingId", msg.breakoutMeetingId)
     payload.put("userId", msg.userId)
     payload.put("joinURL", msg.joinURL)
 
@@ -175,8 +179,8 @@ object MeetingMessageToJsonConverter {
 
   def updateBreakoutUsersOutMessageToJson(msg: UpdateBreakoutUsersOutMessage): String = {
     val payload = new java.util.HashMap[String, Any]()
-    payload.put("meetingId", msg.meetingId)
-    payload.put("breakoutId", msg.breakoutId)
+    payload.put("parentMeetingId", msg.parentMeetingId)
+    payload.put("breakoutMeetingId", msg.breakoutMeetingId)
     payload.put("recorded", msg.recorded)
     payload.put("users", msg.users.toArray)
 
diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
index 97bf4c31003f97f1260c8d961393c0006bf1a98f..0d2f5850d59a527ab8c4f1252abaad5a7d1164b1 100755
--- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
+++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
@@ -4,6 +4,8 @@ trait AppsTestFixtures {
 
   val meetingId = "testMeetingId"
   val externalMeetingId = "testExternalMeetingId"
+  val parentMeetingId = "testParentMeetingId"
+  val sequence = 4
   val meetingName = "test meeting"
   val record = false
   val voiceConfId = "85115"
@@ -16,13 +18,15 @@ trait AppsTestFixtures {
   val createTime = System.currentTimeMillis
   val createDate = "Oct 26, 2015"
   val isBreakout = false
+  val red5DeskShareIP = "127.0.0.1"
+  val red5DeskShareApp = "red5App"
 
-  val mProps = new MeetingProperties(meetingId, externalMeetingId,
+  val mProps = new MeetingProperties(meetingId, externalMeetingId, parentMeetingId,
     meetingName, record,
     voiceConfId, deskshareConfId,
     durationInMinutes,
     autoStartRecording, allowStartStopRecording,
     moderatorPassword, viewerPassword,
-    createTime, createDate, isBreakout)
-
-}
\ No newline at end of file
+    createTime, createDate, red5DeskShareIP, red5DeskShareApp,
+    isBreakout, sequence)
+}
diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala
index a84000b5e4b9e8447195cb56e72a06320d702054..c980b35d665eb0aa27c6cf51c8806c864da7d0c0 100755
--- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala
+++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/apps/BreakoutRoomsUtilSpec.scala
@@ -6,18 +6,11 @@ import org.bigbluebutton.core.UnitSpec
 
 class BreakoutRoomsUtilSpec extends UnitSpec {
 
-  it should "return a pdfURL" in {
-    val baseURL = "http://localhost/pre1/page1."
-    val swfURL = baseURL + "swf"
-    val pdfURL = BreakoutRoomsUtil.fromSWFtoPDF(swfURL)
-    assert(pdfURL == baseURL + "pdf")
-  }
-
   it should "return a meetingId" in {
     val mainMeetingId = "abc-123"
     val index = 1
     val result = mainMeetingId.concat("-").concat(index.toString())
-    val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mainMeetingId, index)
+    val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mainMeetingId, index)
     assert(breakoutMeetingId == result)
   }
 
diff --git a/akka-bbb-fsesl/build.sbt b/akka-bbb-fsesl/build.sbt
index 2f751e0e5de5f8360d93668b9d5a42c5e9aed122..2d99dde59877d0cab3da07d65d523a1909740fd7 100755
--- a/akka-bbb-fsesl/build.sbt
+++ b/akka-bbb-fsesl/build.sbt
@@ -47,7 +47,7 @@ libraryDependencies ++= {
     "org.pegdown" 		      %  "pegdown"           % "1.4.0",
     "junit" 				      %  "junit"             % "4.11",
     "com.etaty.rediscala"      %%  "rediscala"         % "1.4.0",
-    "commons-codec"             %  "commons-codec"     % "1.8",
+    "commons-codec"             %  "commons-codec"     % "1.10",
     "joda-time"                 %  "joda-time"         % "2.3",
     "com.google.code.gson"      %  "gson"              % "1.7.1",
     "redis.clients"             %  "jedis"             % "2.1.0",
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java
index d2ecfc2881d4afaf374117419b8f9b926a7b1c25..48fbdb56281b809d148c5a5c7e6a84ec772cc296 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/CreateMeetingRequest.java
@@ -3,48 +3,54 @@ package org.bigbluebutton.messages;
 import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
 
 public class CreateMeetingRequest implements IBigBlueButtonMessage {
-  public final static String NAME = "CreateMeetingRequest";
-  
-  public final Header header;
-  public final CreateMeetingRequestPayload payload;
-  
-  public CreateMeetingRequest(CreateMeetingRequestPayload payload) {
-    this.header = new Header(NAME);
-    this.payload = payload;
-  }
-  
-  public static class CreateMeetingRequestPayload {
-    public final String id;
-    public final String externalId;
-    public final String name;
-    public final Boolean record;
-    public final String voiceConfId;
-    public final Integer durationInMinutes;
-    public final Boolean autoStartRecording;
-    public final Boolean allowStartStopRecording;
-    public final String moderatorPassword;
-    public final String viewerPassword;
-    public final Long createTime;
-    public final String createDate;
-    public final Boolean isBreakout;
-    
-    public CreateMeetingRequestPayload(String id, String externalId, String name, Boolean record, String voiceConfId, 
-        Integer duration, Boolean autoStartRecording, 
-        Boolean allowStartStopRecording, String moderatorPass,
-        String viewerPass, Long createTime, String createDate, Boolean isBreakout) {
-      this.id = id;
-      this.externalId = externalId;
-      this.name = name;
-      this.record = record;
-      this.voiceConfId = voiceConfId;
-      this.durationInMinutes = duration; 
-      this.autoStartRecording = autoStartRecording;
-      this.allowStartStopRecording = allowStartStopRecording;
-      this.moderatorPassword = moderatorPass;
-      this.viewerPassword = viewerPass;
-      this.createTime = createTime;
-      this.createDate = createDate;
-      this.isBreakout = isBreakout;
+    public final static String NAME = "CreateMeetingRequest";
+
+    public final Header header;
+    public final CreateMeetingRequestPayload payload;
+
+    public CreateMeetingRequest(CreateMeetingRequestPayload payload) {
+        this.header = new Header(NAME);
+        this.payload = payload;
+    }
+
+    public static class CreateMeetingRequestPayload {
+        public final String id;
+        public final String externalId;
+        public final String parentId;
+        public final String name;
+        public final Boolean record;
+        public final String voiceConfId;
+        public final Integer durationInMinutes;
+        public final Boolean autoStartRecording;
+        public final Boolean allowStartStopRecording;
+        public final String moderatorPassword;
+        public final String viewerPassword;
+        public final Long createTime;
+        public final String createDate;
+        public final Boolean isBreakout;
+        public final Integer sequence;
+
+        public CreateMeetingRequestPayload(String id, String externalId,
+                String parentId, String name, Boolean record,
+                String voiceConfId, Integer duration,
+                Boolean autoStartRecording, Boolean allowStartStopRecording,
+                String moderatorPass, String viewerPass, Long createTime,
+                String createDate, Boolean isBreakout, Integer sequence) {
+            this.id = id;
+            this.externalId = externalId;
+            this.parentId = parentId;
+            this.name = name;
+            this.record = record;
+            this.voiceConfId = voiceConfId;
+            this.durationInMinutes = duration;
+            this.autoStartRecording = autoStartRecording;
+            this.allowStartStopRecording = allowStartStopRecording;
+            this.moderatorPassword = moderatorPass;
+            this.viewerPassword = viewerPass;
+            this.createTime = createTime;
+            this.createDate = createDate;
+            this.isBreakout = isBreakout;
+            this.sequence = sequence;
+        }
     }
-  }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java
index 406a6c1a543f51d3a618ba89e69212c4bb37f480..20509fba10c478e9bbd4c419c4405c14146aebcc 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomJoinURLPayload.java
@@ -20,14 +20,14 @@ package org.bigbluebutton.messages.payload;
 
 public class BreakoutRoomJoinURLPayload {
 
-  public final String meetingId;
-  public final String breakoutId;
+  public final String parentMeetingId;
+  public final String breakoutMeetingId;
   public final String userId;
   public final String joinURL;
   
-  public BreakoutRoomJoinURLPayload(String meetingId, String breakoutId, String userId, String joinURL) {
-    this.meetingId = meetingId;
-    this.breakoutId = breakoutId;
+  public BreakoutRoomJoinURLPayload(String parentMeetingId, String breakoutMeetingId, String userId, String joinURL) {
+    this.parentMeetingId = parentMeetingId;
+    this.breakoutMeetingId = breakoutMeetingId;
     this.userId = userId;
     this.joinURL = joinURL;
   }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java
index b21d95e8368a7dd9b59fd7558cd6349ce19ef495..898ab5048690b8fbd91aedcabed86f87af040891 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomPayload.java
@@ -2,13 +2,18 @@ package org.bigbluebutton.messages.payload;
 
 public class BreakoutRoomPayload {
 
-	public final String meetingId;
-	public final String breakoutId;
-	public final String name;
+    public final String parentMeetingId;
+    public final String meetingId;
+    public final String externalMeetingId;
+    public final String name;
+    public final Integer sequence;
 
-	public BreakoutRoomPayload(String meetingId, String breakoutId, String name) {
-		this.meetingId = meetingId;
-		this.breakoutId = breakoutId;
-		this.name = name;
-	}
+    public BreakoutRoomPayload(String parentMeetingId, String meetingId,
+            String externalMeetingId, String name, Integer sequence) {
+        this.parentMeetingId = parentMeetingId;
+        this.meetingId = meetingId;
+        this.externalMeetingId = externalMeetingId;
+        this.name = name;
+        this.sequence = sequence;
+    }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java
index 87336f3f3dca54be00820e671bdbbd986932636b..20cceb76bcdd1b3b587513536bdcbc17338cbb37 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/BreakoutRoomRequestPayload.java
@@ -3,13 +3,16 @@ package org.bigbluebutton.messages.payload;
 import java.util.ArrayList;
 
 public class BreakoutRoomRequestPayload {
-  // Name of the breakout room
-  public final String name;
-  // List of user ids to assign to the breakout room
-  public final ArrayList<String> users;
-  
-  public BreakoutRoomRequestPayload(String name, ArrayList<String> users) {
-    this.name = name;
-    this.users = users;
-  }
+    // Name of the breakout room
+    public final String name;
+    // Sequence of the breakout room
+    public final Integer sequence;
+    // List of user ids to assign to the breakout room
+    public final ArrayList<String> users;
+
+    public BreakoutRoomRequestPayload(String name, Integer sequence, ArrayList<String> users) {
+        this.name = name;
+        this.sequence = sequence;
+        this.users = users;
+    }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java
index b69987cd9de4b54f3173db64558254b65a62087e..9c49157c76b5ee750a8b01d3707fce95ede2682d 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomRequestPayload.java
@@ -1,9 +1,10 @@
 package org.bigbluebutton.messages.payload;
 
 public class CreateBreakoutRoomRequestPayload {
-    public final String breakoutId;
-    public final String parentId; // The main meeting internal id
+    public final String breakoutMeetingId;
+    public final String parentMeetingId; // The main meeting internal id
     public final String name; // The name of the breakout room
+    public final Integer sequence; // The sequnce number of the breakout room
     public final String voiceConfId; // The voice conference id
     public final String viewerPassword;
     public final String moderatorPassword;
@@ -12,14 +13,15 @@ public class CreateBreakoutRoomRequestPayload {
     public final Integer sourcePresentationSlide;
     public final Boolean record;
 
-    public CreateBreakoutRoomRequestPayload(String breakoutId, String parentId,
-            String name, String voiceConfId, String viewerPassword,
+    public CreateBreakoutRoomRequestPayload(String meetingMeetingId, String parentMeetingId,
+            String name, Integer sequence, String voiceConfId, String viewerPassword,
             String moderatorPassword, Integer duration,
             String sourcePresentationId, Integer sourcePresentationSlide,
             Boolean record) {
-        this.breakoutId = breakoutId;
-        this.parentId = parentId;
+        this.breakoutMeetingId = meetingMeetingId;
+        this.parentMeetingId = parentMeetingId;
         this.name = name;
+        this.sequence = sequence;
         this.voiceConfId = voiceConfId;
         this.viewerPassword = viewerPassword;
         this.moderatorPassword = moderatorPassword;
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java
index 33e020df1994b07b35bf421856d3c6d3667ed935..35038b6e0cf8e17d4a8a75bb6a58acf0d4d23f62 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/CreateBreakoutRoomsRequestPayload.java
@@ -3,21 +3,24 @@ package org.bigbluebutton.messages.payload;
 import java.util.ArrayList;
 
 public class CreateBreakoutRoomsRequestPayload {
-  // The main meeting internal id
-  public final String meetingId;
-  // The list of breakout rooms
-  public final ArrayList<BreakoutRoomRequestPayload> rooms; 
-  // The duration of the breakout room
-  public final Integer durationInMinutes;
-  // Breakout rooms recording option 
-  public final Boolean record;
+    // The main meeting internal id
+    public final String meetingId;
+    // The list of breakout rooms
+    public final ArrayList<BreakoutRoomRequestPayload> rooms;
+    // The duration of the breakout room
+    public final Integer durationInMinutes;
+    // Breakout rooms recording option
+    public final Boolean record;
+    // Creates join URL with redirect value true or false
+    public final Boolean redirectOnJoin;
 
-  public CreateBreakoutRoomsRequestPayload(String meetingId, 
-		  ArrayList<BreakoutRoomRequestPayload> breakoutRooms, 
-		  Integer duration, Boolean record) {
-    this.meetingId = meetingId;
-    this.rooms = breakoutRooms;
-    this.durationInMinutes = duration;
-    this.record = record;
-  }
+    public CreateBreakoutRoomsRequestPayload(String meetingId,
+            ArrayList<BreakoutRoomRequestPayload> breakoutRooms,
+            Integer duration, Boolean record, Boolean redirectOnJoin) {
+        this.meetingId = meetingId;
+        this.rooms = breakoutRooms;
+        this.durationInMinutes = duration;
+        this.record = record;
+        this.redirectOnJoin = redirectOnJoin;
+    }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java
index 1d3d3c51aa60020e112304bfee0cc9362a048532..aeed47d5185e8601a8111fb55ce66a0fcbb6cb4f 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/ListenInOnBreakoutPayload.java
@@ -2,14 +2,14 @@ package org.bigbluebutton.messages.payload;
 
 public class ListenInOnBreakoutPayload {
 
-	public final String meetingId;
-	public final String targetMeetingId;
-	public final String userId;
+    public final String meetingId;
+    public final String targetMeetingId;
+    public final String userId;
 
-	public ListenInOnBreakoutPayload(String meetingId, String breakoutId,
-			String userId) {
-		this.meetingId = meetingId;
-		this.targetMeetingId = breakoutId;
-		this.userId = userId;
-	}
+    public ListenInOnBreakoutPayload(String meetingId, String targetMeetingId,
+            String userId) {
+        this.meetingId = meetingId;
+        this.targetMeetingId = targetMeetingId;
+        this.userId = userId;
+    }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java
index 7c698c6c89c4bc1c885f4bf6dd992ce046fffc2e..00f2aabaee9e2cd121cca2eea2ba3609799bc431 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/RequestBreakoutJoinURLPayload.java
@@ -2,13 +2,16 @@ package org.bigbluebutton.messages.payload;
 
 public class RequestBreakoutJoinURLPayload {
 
-  public final String meetingId;
-  public final String breakoutId;
-  public final String userId;
-  
-  public RequestBreakoutJoinURLPayload(String meetingId, String breakoutId, String userId) {
-    this.meetingId = meetingId;
-    this.breakoutId = breakoutId;
-    this.userId = userId;
-  }
+    public final String meetingId;
+    public final String breakoutMeetingId;
+    public final String userId;
+    public final Boolean redirect;
+
+    public RequestBreakoutJoinURLPayload(String meetingId,
+            String breakoutMeetingId, String userId, Boolean redirect) {
+        this.meetingId = meetingId;
+        this.breakoutMeetingId = breakoutMeetingId;
+        this.userId = userId;
+        this.redirect = redirect;
+    }
 }
diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java
index f96d2ca064d988135350b64710a4b32cb9ce6c06..84810e44cacf905f81d284cc1f576caa472904b8 100755
--- a/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java
+++ b/bbb-common-message/src/main/java/org/bigbluebutton/messages/payload/UpdateBreakoutUsersPayload.java
@@ -5,12 +5,12 @@ import java.util.ArrayList;
 public class UpdateBreakoutUsersPayload {
 
 	public final ArrayList<BreakoutUserPayload> users;
-	public final String breakoutId;
-	public final String meetingId;
+	public final String breakoutMeetingId;
+	public final String parentMeetingId;
 
-	public UpdateBreakoutUsersPayload(String meetingId, String breakoutId, ArrayList<BreakoutUserPayload> users) {
-		this.meetingId = meetingId;
-		this.breakoutId = breakoutId;
+	public UpdateBreakoutUsersPayload(String meetingParentId, String breakoutMeetingId, ArrayList<BreakoutUserPayload> users) {
+		this.parentMeetingId = meetingParentId;
+		this.breakoutMeetingId = breakoutMeetingId;
 	this.users = users;
 	}
 }
diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java
index e520494a35dd8a90c9803282da12accc68d1dc8e..59f3086d0d8f12afa9180afc45bf37aa26178606 100755
--- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java
+++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomRequestTest.java
@@ -13,6 +13,7 @@ public class CreateBreakoutRoomRequestTest {
     String parentId = "abc-123";
     Integer durationInMinutes = 20;
     String name = "Breakout room 1";
+    Integer sequence = 3;
     String voiceConfId = "851153";
     String viewerPassword = "vp";
     String moderatorPassword = "mp";
@@ -20,9 +21,10 @@ public class CreateBreakoutRoomRequestTest {
     Integer sourePresentationSlide = 5;
     Boolean record = false;
     
-    CreateBreakoutRoomRequestPayload payload = 
-        new CreateBreakoutRoomRequestPayload(breakoutId, parentId, name, voiceConfId, 
-            viewerPassword, moderatorPassword, durationInMinutes, sourcePresentationId, sourePresentationSlide, record);
+        CreateBreakoutRoomRequestPayload payload = new CreateBreakoutRoomRequestPayload(
+                breakoutId, parentId, name, sequence, voiceConfId,
+                viewerPassword, moderatorPassword, durationInMinutes,
+                sourcePresentationId, sourePresentationSlide, record);
     CreateBreakoutRoomRequest msg = new CreateBreakoutRoomRequest(payload);    
     Gson gson = new Gson();
     String json = gson.toJson(msg);
@@ -31,8 +33,9 @@ public class CreateBreakoutRoomRequestTest {
     CreateBreakoutRoomRequest rxMsg = gson.fromJson(json, CreateBreakoutRoomRequest.class);
     
     Assert.assertEquals(rxMsg.header.name, CreateBreakoutRoomRequest.NAME);
-    Assert.assertEquals(rxMsg.payload.breakoutId, breakoutId);
+    Assert.assertEquals(rxMsg.payload.breakoutMeetingId, breakoutId);
     Assert.assertEquals(rxMsg.payload.name, name);
+    Assert.assertEquals(rxMsg.payload.sequence, sequence);
     Assert.assertEquals(rxMsg.payload.voiceConfId, voiceConfId);
     Assert.assertEquals(rxMsg.payload.viewerPassword, viewerPassword);
     Assert.assertEquals(rxMsg.payload.moderatorPassword, moderatorPassword);
diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java
index 48c91e0b9f6e77aec85f8d6e62c35c2a08146a03..c7ee9eb2534043f4b73ebf727c5d6132147c883c 100755
--- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java
+++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateBreakoutRoomsRequestTest.java
@@ -15,23 +15,24 @@ public class CreateBreakoutRoomsRequestTest {
     String meetingId = "abc123";
     Integer durationInMinutes = 20;
     Boolean record = true;
+    Boolean redirectOnJoin = false;
     
     ArrayList<String> room1Users = new ArrayList<String>();
     room1Users.add("Tidora"); room1Users.add("Nidora"); room1Users.add("Tinidora");
-    BreakoutRoomRequestPayload room1 = new BreakoutRoomRequestPayload("room1", room1Users);
+    BreakoutRoomRequestPayload room1 = new BreakoutRoomRequestPayload("room1", 1, room1Users);
     
     ArrayList<String> room2Users = new ArrayList<String>();
     room2Users.add("Jose"); room2Users.add("Wally"); room2Users.add("Paolo");
-    BreakoutRoomRequestPayload room2= new BreakoutRoomRequestPayload("room2", room2Users);
+    BreakoutRoomRequestPayload room2= new BreakoutRoomRequestPayload("room2", 2, room2Users);
     
     ArrayList<String> room3Users = new ArrayList<String>();
     room3Users.add("Alden"); room3Users.add("Yaya Dub");
-    BreakoutRoomRequestPayload room3= new BreakoutRoomRequestPayload("room3", room3Users);
+    BreakoutRoomRequestPayload room3= new BreakoutRoomRequestPayload("room3", 3, room3Users);
     
     ArrayList<BreakoutRoomRequestPayload> rooms = new ArrayList<BreakoutRoomRequestPayload>();
     rooms.add(room1); rooms.add(room2); rooms.add(room3);
     
-    CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, record);
+    CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, record, redirectOnJoin);
     CreateBreakoutRoomsRequest msg = new CreateBreakoutRoomsRequest(payload);    
     Gson gson = new Gson();
     String json = gson.toJson(msg);
diff --git a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java
index 476f8efbe2fd05078925a1bb86d82ca4ce846408..e25dfa81c73e13029390fb6fa69acee8a1879b46 100755
--- a/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java
+++ b/bbb-common-message/src/test/java/org/bigbluebutton/messages/CreateMeetingRequestTest.java
@@ -12,6 +12,7 @@ public class CreateMeetingRequestTest {
   public void testCreateMeetingRequest() {
     String meetingId = "abc123";
     String externalId = "extabc123";
+    String parentId = "";
     Boolean record = false;
     Integer durationInMinutes = 20;
     String name = "Breakout room 1";
@@ -19,16 +20,17 @@ public class CreateMeetingRequestTest {
     Boolean autoStartRecording = false;
     Boolean allowStartStopRecording = false;
     Boolean isBreakout = true;
+    Integer sequence = 4;
     String viewerPassword = "vp";
     String moderatorPassword = "mp";
     long createTime = System.currentTimeMillis();
     String createDate = new Date(createTime).toString();
     
-    CreateMeetingRequestPayload payload = 
-        new CreateMeetingRequestPayload(meetingId, externalId, name, record, voiceConfId, 
-            durationInMinutes, autoStartRecording, 
-            allowStartStopRecording, moderatorPassword,
-            viewerPassword, createTime, createDate, isBreakout);
+        CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload(
+                meetingId, externalId, parentId, name, record, voiceConfId,
+                durationInMinutes, autoStartRecording, allowStartStopRecording,
+                moderatorPassword, viewerPassword, createTime, createDate,
+                isBreakout, sequence);
     CreateMeetingRequest msg = new CreateMeetingRequest(payload);    
     Gson gson = new Gson();
     String json = gson.toJson(msg);
@@ -38,11 +40,14 @@ public class CreateMeetingRequestTest {
     
     Assert.assertEquals(rxMsg.header.name, CreateMeetingRequest.NAME);
     Assert.assertEquals(rxMsg.payload.id, meetingId);
+    Assert.assertEquals(rxMsg.payload.externalId, externalId);
+    Assert.assertEquals(rxMsg.payload.parentId, parentId);
     Assert.assertEquals(rxMsg.payload.name, name);
     Assert.assertEquals(rxMsg.payload.voiceConfId, voiceConfId);
     Assert.assertEquals(rxMsg.payload.viewerPassword, viewerPassword);
     Assert.assertEquals(rxMsg.payload.moderatorPassword, moderatorPassword);
     Assert.assertEquals(rxMsg.payload.durationInMinutes, durationInMinutes);
     Assert.assertEquals(rxMsg.payload.isBreakout, isBreakout);
+    Assert.assertEquals(rxMsg.payload.sequence, sequence);
   }
 }
diff --git a/bbb-screenshare/app/build.sbt b/bbb-screenshare/app/build.sbt
index c1a489f77218274e048177bbdcf3600de18b039b..5e3ad2fc20115b55dfdaf42b2acceb2b262f2a34 100644
--- a/bbb-screenshare/app/build.sbt
+++ b/bbb-screenshare/app/build.sbt
@@ -44,7 +44,7 @@ libraryDependencies ++= {
     //    "org.pegdown"               %  "pegdown"           % "1.4.0",
     //    "junit"                     %  "junit"             % "4.11",
     //    "com.etaty.rediscala"      %%  "rediscala"         % "1.4.0",
-    "commons-codec"             %  "commons-codec"     % "1.8",
+    "commons-codec"             %  "commons-codec"     % "1.10",
         "redis.clients"             %  "jedis"             % "2.7.2",
     //    "org.apache.commons"        %  "commons-lang3"     % "3.2",
     "org.apache.commons"        %  "commons-pool2"     % "2.3",
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java
index 57784c825f445152fabc8c0cfe3f60a7d46eccfe..b94374355f5757a73aa374c67a10442eeb69a222 100644
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/DeskShareMessageSender.java
@@ -3,12 +3,11 @@ package org.bigbluebutton.red5.client;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.bigbluebutton.common.messages.ChatKeyUtil;
-import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage;
 import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage;
+import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage;
 import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
-import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
 import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
+import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
 
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java
index ec68c343948d21e7aa09531088897b3bc22851d3..7990274ade0d71bc58c8227845de825f1da21f6f 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/UserClientMessageSender.java
@@ -44,7 +44,6 @@ import com.google.gson.Gson;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 
-
 public class UserClientMessageSender {
   private static Logger log = Red5LoggerFactory.getLogger(UserClientMessageSender.class, "bigbluebutton");
 
@@ -543,8 +542,8 @@ public class UserClientMessageSender {
   
   private void processBreakoutRoomJoinURL(BreakoutRoomJoinURL msg) {
 	  Map<String, Object> args = new HashMap<String, Object>();	
-	  args.put("meetingId", msg.payload.meetingId);
-	  args.put("breakoutId", msg.payload.breakoutId);
+	  args.put("parentMeetingId", msg.payload.parentMeetingId);
+	  args.put("breakoutMeetingId", msg.payload.breakoutMeetingId);
 	  args.put("userId", msg.payload.userId);
 	  args.put("joinURL", msg.payload.joinURL);
 	  
@@ -552,7 +551,7 @@ public class UserClientMessageSender {
       Gson gson = new Gson();
       message.put("msg", gson.toJson(args));
       
-      DirectClientMessage m = new DirectClientMessage(msg.payload.meetingId, msg.payload.userId, "breakoutRoomJoinURL", message);
+      DirectClientMessage m = new DirectClientMessage(msg.payload.parentMeetingId, msg.payload.userId, "breakoutRoomJoinURL", message);
       service.sendMessage(m);
   }
   
@@ -584,42 +583,44 @@ public class UserClientMessageSender {
   
   private void processUpdateBreakoutUsers(UpdateBreakoutUsers msg) {
 	  Map<String, Object> args = new HashMap<String, Object>();	
-	  args.put("meetingId", msg.payload.meetingId);
-	  args.put("breakoutId", msg.payload.breakoutId);
+	  args.put("parentMeetingId", msg.payload.parentMeetingId);
+	  args.put("breakoutMeetingId", msg.payload.breakoutMeetingId);
 	  args.put("users", msg.payload.users);
 	  
 	  Map<String, Object> message = new HashMap<String, Object>();
 	  Gson gson = new Gson();
 	  message.put("msg", gson.toJson(args));
       
-	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "updateBreakoutUsers", message);
+	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "updateBreakoutUsers", message);
       service.sendMessage(m);
   }
   
   private void processBreakoutRoomStarted(BreakoutRoomStarted msg) {
 	  Map<String, Object> args = new HashMap<String, Object>();	
-	  args.put("breakoutId", msg.payload.breakoutId);
-	  args.put("meetingId", msg.payload.meetingId);
+	  args.put("breakoutMeetingId", msg.payload.meetingId);
+      args.put("parentMeetingId", msg.payload.parentMeetingId);
+      args.put("externalMeetingId", msg.payload.externalMeetingId);
+	  args.put("sequence", msg.payload.sequence);
 	  args.put("name", msg.payload.name);
 	  
 	  Map<String, Object> message = new HashMap<String, Object>();
 	  Gson gson = new Gson();
 	  message.put("msg", gson.toJson(args));
 	  
-	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "breakoutRoomStarted", message);
+	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "breakoutRoomStarted", message);
       service.sendMessage(m);
   }
   
   private void processBreakoutRoomClosed(BreakoutRoomClosed msg) {
 	  Map<String, Object> args = new HashMap<String, Object>();	
-	  args.put("breakoutId", msg.payload.breakoutId);
-	  args.put("meetingId", msg.payload.meetingId);
+	  args.put("breakoutMeetingId", msg.payload.meetingId);
+	  args.put("parentMeetingId", msg.payload.parentMeetingId);
 	  
 	  Map<String, Object> message = new HashMap<String, Object>();
 	  Gson gson = new Gson();
 	  message.put("msg", gson.toJson(args));
 	  
-	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.meetingId, "breakoutRoomClosed", message);
+	  BroadcastClientMessage m = new BroadcastClientMessage(msg.payload.parentMeetingId, "breakoutRoomClosed", message);
       service.sendMessage(m);
   }
 }
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java
index 510f88bcea1efbce2fe7a6ff047d675baecfed72..792ee0645a08f01b500d3036f4185f35071eba9a 100755
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/WhiteboardClientMessageSender.java
@@ -1,6 +1,5 @@
 package org.bigbluebutton.red5.client;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java
index f16fdc33c75c1f0f97cca070b51c98f432d42b53..fa03834a876d1be42897c45306306a6aab7b5591 100644
--- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java
+++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/DesktopSharingService.java
@@ -18,9 +18,6 @@
 */
 package org.bigbluebutton.red5.service;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import org.bigbluebutton.red5.BigBlueButtonSession;
 import org.bigbluebutton.red5.Constants;
 import org.bigbluebutton.red5.pubsub.MessagePublisher;
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as
index c7201daa9d1c38d9326884dc92ab0cfd68ee8bd3..58ac6998b144bf6ecd1edf1090fe4ae90543d76a 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/BreakoutRoomEvent.as
@@ -39,7 +39,9 @@ package org.bigbluebutton.main.events {
 
 		public var meetingId:String;
 
-		public var breakoutId:String;
+		public var breakoutMeetingId:String;
+		
+		public var breakoutMeetingSequence:int;
 
 		public var rooms:Array;
 
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as
index 7d302e8339b4846f212755ae4de4126d9446b774..4623400cb483865f569433bb08d5bfb5a35a9731 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BBBUser.as
@@ -334,12 +334,11 @@ package org.bigbluebutton.main.model.users
 		private var _breakoutRooms : Array = [];
 		
 		[Bindable("displayNameChange")]
-		public function get displayName() : String {
-			if (ArrayUtils.isEmpty(_breakoutRooms)){
+		public function get displayName():String {
+			if (ArrayUtils.isEmpty(_breakoutRooms)) {
 				return name;
-			}
-			else {
-				return "[" + _breakoutRooms.join(",") + "] " +name;
+			} else {
+				return "[" + _breakoutRooms.join(",") + "] " + name;
 			}
 		}
 
@@ -352,14 +351,14 @@ package org.bigbluebutton.main.model.users
 			dispatchEvent(new Event("displayNameChange"));
 		}
 
-		public function addBreakoutRoom(roomNumber:String):void {
+		public function addBreakoutRoom(roomNumber:int):void {
 			if (!ArrayUtils.contains(_breakoutRooms, roomNumber)) {
 				_breakoutRooms.push(roomNumber);
 				dispatchEvent(new Event("displayNameChange"));
 			}
 		}
 
-		public function removeBreakoutRoom(roomNumber:String):void {
+		public function removeBreakoutRoom(roomNumber:int):void {
 			_breakoutRooms.splice(_breakoutRooms.indexOf(roomNumber), 1);
 			dispatchEvent(new Event("displayNameChange"));
 		}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as
index b94a55ee3d22d39cf4bd5e058422b1555e65a572..ed653efc1343a62c351ef171e16f356ef9a9e069 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/BreakoutRoom.as
@@ -28,7 +28,11 @@ package org.bigbluebutton.main.model.users {
 		
 		public static const OTHER:String = "other";
 		
-		public var breakoutId:String;
+		public var externalMeetingId:String;
+		
+		public var meetingId:String;
+		
+		public var sequence:int;
 		
 		public var name:String;
 		
@@ -44,6 +48,5 @@ package org.bigbluebutton.main.model.users {
 		public function get numberOfUsers():int {
 			return users.length;
 		}
-
 	}
 }
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
index c899cfc0d009a9c63fdb8936b8abaaf2eb3d4247..61022aa41a66fe7c82fe9f8a4cc8a44d0b5f4744 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/Conference.as
@@ -20,6 +20,7 @@ package org.bigbluebutton.main.model.users {
 	
 	import mx.collections.ArrayCollection;
 	import mx.collections.Sort;
+	import mx.collections.SortField;
 	
 	import org.as3commons.lang.ArrayUtils;
 	import org.as3commons.lang.StringUtils;
@@ -534,59 +535,87 @@ package org.bigbluebutton.main.model.users {
 		
 		/* Breakout room feature */
 		public function addBreakoutRoom(newRoom:BreakoutRoom):void {
-			if (hasBreakoutRoom(newRoom.breakoutId)) {
-				removeBreakoutRoom(newRoom.breakoutId);
+			if (hasBreakoutRoom(newRoom.meetingId)) {
+				removeBreakoutRoom(newRoom.meetingId);
 			}
 			breakoutRooms.addItem(newRoom);
+			sortBreakoutRooms();
+		}
+		
+		private function sortBreakoutRooms() : void {
+			var sort:Sort = new Sort();
+			sort.fields = [new SortField("sequence", true, false, true)];
+			breakoutRooms.sort = sort;
 			breakoutRooms.refresh();
 		}
 
-		public function updateBreakoutRoomUsers(breakoutId:String, breakoutUsers:Array):void {
-			var room:Object = getBreakoutRoom(breakoutId);
+		public function updateBreakoutRoomUsers(breakoutMeetingId:String, breakoutUsers:Array):void {
+			var room:BreakoutRoom = getBreakoutRoom(breakoutMeetingId);
 			if (room != null) {
-				BreakoutRoom(room).users = new ArrayCollection(breakoutUsers);
-				var breakoutRoomNumber:String = StringUtils.substringAfterLast(breakoutId, "-");
+				room.users = new ArrayCollection(breakoutUsers);
 				var updateUsers:Array = [];
 				// Update users breakout rooms
-				var user : BBBUser;
+				var user:BBBUser;
 				for (var i:int = 0; i < breakoutUsers.length; i++) {
 					var userId:String = StringUtils.substringBeforeLast(breakoutUsers[i].id, "-");
 					user = getUser(userId);
 					if (user) {
-						user.addBreakoutRoom(breakoutRoomNumber)
+						user.addBreakoutRoom(room.sequence)
 					}
 					updateUsers.push(userId);
 				}
 				// Remove users breakout rooms if the users left the breakout rooms
 				for (var j:int = 0; j < users.length; j++) {
 					user = BBBUser(users.getItemAt(j));
-					if (updateUsers.indexOf(BBBUser(users.getItemAt(j)).userID) == -1 && ArrayUtils.contains(user.breakoutRooms, breakoutRoomNumber)) {
-						user.removeBreakoutRoom(breakoutRoomNumber);
+					if (updateUsers.indexOf(BBBUser(users.getItemAt(j)).userID) == -1 && ArrayUtils.contains(user.breakoutRooms, room.sequence)) {
+						user.removeBreakoutRoom(room.sequence);
 					}
 				}
 				users.refresh();
 			}
 		}
-		
+
 		/**
-		 * Returns a breakout room by its breakoutId
+		 * Returns a breakout room by its internal meeting ID
 		 */
-		public function getBreakoutRoom(breakoutId:String):BreakoutRoom {
-			var r:Object = getBreakoutRoomIndex(breakoutId);
+		public function getBreakoutRoom(breakoutMeetingId:String):BreakoutRoom {
+			var r:Object = getBreakoutRoomIndex(breakoutMeetingId);
 			if (r != null) {
 				return r.room as BreakoutRoom;
 			}
 			return null;
 		}
 		
+		public function getBreakoutRoomByExternalId(externalId:String):BreakoutRoom {
+			var aRoom:BreakoutRoom;
+			for (var i:int = 0; i < breakoutRooms.length; i++) {
+				aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom;
+				if (aRoom.externalMeetingId == externalId) {
+					return aRoom;
+				}
+			}
+			return null;
+		}
+		
+		public function getBreakoutRoomBySequence(sequence:int):BreakoutRoom {
+			var aRoom:BreakoutRoom;
+			for (var i:int = 0; i < breakoutRooms.length; i++) {
+				aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom;
+				if (aRoom.sequence == sequence) {
+					return aRoom;
+				}
+			}
+			return null;
+		}
+
 		/**
-		 * Finds the index of a breakout room by its breakoutId
+		 * Finds the index of a breakout room by its internal meeting ID
 		 */
-		public function getBreakoutRoomIndex(breakoutId:String):Object {
+		public function getBreakoutRoomIndex(breakoutMeetingId:String):Object {
 			var aRoom:BreakoutRoom;
 			for (var i:int = 0; i < breakoutRooms.length; i++) {
 				aRoom = breakoutRooms.getItemAt(i) as BreakoutRoom;
-				if (aRoom.breakoutId == breakoutId) {
+				if (aRoom.meetingId == breakoutMeetingId) {
 					return {index: i, room: aRoom};
 				}
 			}
@@ -594,39 +623,38 @@ package org.bigbluebutton.main.model.users {
 			return null;
 		}
 
-		public function removeBreakoutRoom(breakoutId:String):void {
-			var p:Object = getBreakoutRoomIndex(breakoutId);
-			if (p != null) {
-				breakoutRooms.removeItemAt(p.index);
-				breakoutRooms.refresh();
+		public function removeBreakoutRoom(breakoutMeetingId:String):void {
+			var room:Object = getBreakoutRoomIndex(breakoutMeetingId);
+			if (room != null) {
+				breakoutRooms.removeItemAt(room.index);
+				sortBreakoutRooms();
 				if (breakoutRooms.length == 0) {
 					breakoutRoomsReady = false;
 				}
 				// Remove breakout room number display from users
 				for (var i:int; i < users.length; i++) {
-					var breakoutRoomNumber:String = StringUtils.substringAfterLast(breakoutId, "-");
-					if (ArrayUtils.contains(users[i].breakoutRooms, breakoutRoomNumber)) {
-						users[i].removeBreakoutRoom(breakoutRoomNumber);
+					if (ArrayUtils.contains(users[i].breakoutRooms, room.room.sequence)) {
+						users[i].removeBreakoutRoom(room.room.sequence);
 					}
 				}
 				users.refresh();
 			}
 		}
-		
-		public function hasBreakoutRoom(breakoutId:String):Boolean {
-			var p:Object = getBreakoutRoomIndex(breakoutId);
+
+		public function hasBreakoutRoom(breakoutMeetingId:String):Boolean {
+			var p:Object = getBreakoutRoomIndex(breakoutMeetingId);
 			if (p != null) {
 				return true;
 			}
 			return false;
 		}
 		
-		public function setBreakoutRoomInListen(listen:Boolean, breakoutId:String):void {
+		public function setBreakoutRoomInListen(listen:Boolean, breakoutMeetingId:String):void {
 			for (var i:int = 0; i < breakoutRooms.length; i++) {
 				var br:BreakoutRoom = BreakoutRoom(breakoutRooms.getItemAt(i));
 				if (listen == false) {
 					br.listenStatus = BreakoutRoom.NONE;
-				} else if (listen == true && br.breakoutId == breakoutId) {
+				} else if (listen == true && br.meetingId == breakoutMeetingId) {
 					br.listenStatus = BreakoutRoom.SELF;
 				} else {
 					br.listenStatus = BreakoutRoom.OTHER;
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as
index c7900216e497b9cdfa2901330fa3a527e611055d..67196559c60392a470b971955fc5092738d5bc84 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/UserService.as
@@ -199,20 +199,20 @@ package org.bigbluebutton.main.model.users
 		}
 		
 		public function createBreakoutRooms(e:BreakoutRoomEvent):void{
-			sender.createBreakoutRooms(_conferenceParameters.meetingID, e.rooms, e.durationInMinutes, e.record);
+			sender.createBreakoutRooms(_conferenceParameters.meetingID, e.rooms, e.durationInMinutes, e.record, true);
 		}
 		
 		public function requestBreakoutJoinUrl(e:BreakoutRoomEvent):void{
-			sender.requestBreakoutJoinUrl(_conferenceParameters.meetingID, e.breakoutId, e.userId);
+			sender.requestBreakoutJoinUrl(_conferenceParameters.meetingID, e.breakoutMeetingId, e.userId, true);
 		}
 		
 		public function listenInOnBreakout(e:BreakoutRoomEvent):void {
 			if (e.listen) {
-				sender.listenInOnBreakout(_conferenceParameters.meetingID, e.breakoutId, _conferenceParameters.userid);
+				sender.listenInOnBreakout(_conferenceParameters.meetingID, e.breakoutMeetingId, _conferenceParameters.userid);
 			} else {
-				sender.listenInOnBreakout(e.breakoutId, _conferenceParameters.meetingID, _conferenceParameters.userid);
+				sender.listenInOnBreakout(e.breakoutMeetingId, _conferenceParameters.meetingID, _conferenceParameters.userid);
 			}
-			UserManager.getInstance().getConference().setBreakoutRoomInListen(e.listen, e.breakoutId);
+			UserManager.getInstance().getConference().setBreakoutRoomInListen(e.listen, e.breakoutMeetingId);
 		}
 
 		public function endAllBreakoutRooms(e:BreakoutRoomEvent):void {
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 4c40e2923b283328d5c4e5bb9a5d439ffb2b194e..a7d3379ed7e6890ba6fb44370d6ac2ab1cc94f92 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
@@ -46,16 +46,7 @@ package org.bigbluebutton.modules.users.services
   import org.bigbluebutton.main.model.users.IMessageListener;
   import org.bigbluebutton.main.model.users.events.StreamStoppedEvent;
   import org.bigbluebutton.main.model.users.events.UsersConnectionEvent;
-  import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
-  
-  import org.bigbluebutton.modules.present.events.CursorEvent;
-  import org.bigbluebutton.modules.present.events.NavigationEvent;
-  import org.bigbluebutton.modules.present.events.RemovePresentationEvent;
-  import org.bigbluebutton.modules.present.events.UploadEvent;
-  import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
-  import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
   import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
-  import org.bigbluebutton.main.api.JSLog;
   import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
 
   public class MessageReceiver implements IMessageListener
@@ -156,8 +147,8 @@ package org.bigbluebutton.modules.users.services
 	      handleTimeRemainingUpdate(message);
 		  break;
 		case "breakoutRoomsTimeRemainingUpdate":
-			handleBreakoutRoomsTimeRemainingUpdate(message);
-			break;
+		  handleBreakoutRoomsTimeRemainingUpdate(message);
+		  break;
 		case "breakoutRoomStarted":
 		  handleBreakoutRoomStarted(message);
 		  break;
@@ -639,8 +630,10 @@ package org.bigbluebutton.modules.users.services
 		for each(var room : Object in map.rooms)
 		{
 			var breakoutRoom : BreakoutRoom = new BreakoutRoom();
-			breakoutRoom.breakoutId = room.breakoutId;
+			breakoutRoom.meetingId = room.meetingId;
+			breakoutRoom.externalMeetingId = room.externalMeetingId;
 			breakoutRoom.name = room.name;
+			breakoutRoom.sequence = room.sequence;
 			UserManager.getInstance().getConference().addBreakoutRoom(breakoutRoom);
 		}
 		UserManager.getInstance().getConference().breakoutRoomsReady = map.roomsReady;
@@ -650,13 +643,14 @@ package org.bigbluebutton.modules.users.services
 		var map:Object = JSON.parse(msg.msg);
 		var event : BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.BREAKOUT_JOIN_URL);
 		event.joinURL = map.joinURL;
-		event.breakoutId = StringUtils.substringBetween(event.joinURL, "meetingID=", "&");
+		var externalMeetingId : String = StringUtils.substringBetween(event.joinURL, "meetingID=", "&");
+		event.breakoutMeetingSequence = UserManager.getInstance().getConference().getBreakoutRoomByExternalId(externalMeetingId).sequence;
 		dispatcher.dispatchEvent(event);
 	}
 	
 	private function handleUpdateBreakoutUsers(msg:Object):void{
 		var map:Object = JSON.parse(msg.msg);
-		UserManager.getInstance().getConference().updateBreakoutRoomUsers(map.breakoutId, map.users);
+		UserManager.getInstance().getConference().updateBreakoutRoomUsers(map.breakoutMeetingId, map.users);
 	}
 
 	private function handleTimeRemainingUpdate(msg:Object):void {
@@ -676,14 +670,16 @@ package org.bigbluebutton.modules.users.services
 	private function handleBreakoutRoomStarted(msg:Object):void{
 		var map:Object = JSON.parse(msg.msg);	
 		var breakoutRoom : BreakoutRoom = new BreakoutRoom();
-		breakoutRoom.breakoutId = map.breakoutId;
+		breakoutRoom.meetingId = map.breakoutMeetingId;
+		breakoutRoom.externalMeetingId = map.externalMeetingId;
 		breakoutRoom.name = map.name;
+		breakoutRoom.sequence = map.sequence;
 		UserManager.getInstance().getConference().addBreakoutRoom(breakoutRoom);
 	}
 	
 	private function handleBreakoutRoomClosed(msg:Object):void{
 		var map:Object = JSON.parse(msg.msg);	
-		UserManager.getInstance().getConference().removeBreakoutRoom(map.breakoutId);
+		UserManager.getInstance().getConference().removeBreakoutRoom(map.breakoutMeetingId);
 	}
 
   }
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as
index a1ad1da52b03f14f6805970f3ce6def5e40662ac..9881a0fe0f5a13b9fe736c68e6ef0d8c80bddd2e 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as
@@ -87,12 +87,13 @@ package org.bigbluebutton.modules.users.services
 			);
 		}
 		
-		public function createBreakoutRooms(meetingId:String, rooms:Array, durationInMinutes:int, record:Boolean):void {
+		public function createBreakoutRooms(meetingId:String, rooms:Array, durationInMinutes:int, record:Boolean, redirectOnJoin:Boolean):void {
 			var message:Object = new Object();
 			message["meetingId"] = meetingId;
 			message["rooms"] = rooms;
 			message["durationInMinutes"] = durationInMinutes;
 			message["record"] = record;
+			message["redirectOnJoin"] = redirectOnJoin;
 			var jsonMsg:String = JSON.stringify(message);
 			
 			var _nc:ConnectionManager = BBB.initConnectionManager();
@@ -107,24 +108,21 @@ package org.bigbluebutton.modules.users.services
 			);
 		}
 		
-		public function requestBreakoutJoinUrl(meetingId:String, breakoutId:String, userId:String):void {
+		public function requestBreakoutJoinUrl(parentMeetingId:String, breakoutMeetingId:String, userId:String, redirect:Boolean):void {
 			var message:Object = new Object();
-			message["meetingId"] = meetingId;
-			message["breakoutId"] = breakoutId;
+			message["meetingId"] = parentMeetingId;
+			message["breakoutMeetingId"] = breakoutMeetingId;
 			message["userId"] = userId;
+			message["redirect"] = redirect;
 			
 			var jsonMsg:String = JSON.stringify(message);
 			
 			var _nc:ConnectionManager = BBB.initConnectionManager();
-			_nc.sendMessage("breakoutroom.requestBreakoutJoinUrl", function(result:String):void
-			{
+			_nc.sendMessage("breakoutroom.requestBreakoutJoinUrl", function(result:String):void {
 				// On successful result
-			}, function(status:String):void
-			{ // status - On error occurred
+			}, function(status:String):void { // status - On error occurred
 				LOGGER.error(status);
-			},
-			jsonMsg
-			);
+			}, jsonMsg);
 		}
 		
 		public function listenInOnBreakout(meetingId:String, targetMeetingId:String, userId:String):void {
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml
index 13075f7fcda0bb5e9e0a9e219df4d66bd0605e85..8a3b007d526f0f183968b6485aba83c9241d3f0a 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/BreakoutRoomSettings.mxml
@@ -65,15 +65,15 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 				var event:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.CREATE_BREAKOUT_ROOMS);
 				// event.meetingId is filled in the event handler for BreakoutRoomEvent in UserService class
 				event.rooms = new Array();
+				var parentMeetingName:String = UserManager.getInstance().getConference().meetingName;
+				var roomResource:String = ResourceUtil.getInstance().getString('bbb.users.breakout.room');
 				for (var i:int = 0; i < (roomsCombo.selectedIndex + 2); i++) {
 					var users:Array = BreakoutList(roomsContainer.getChildAt(i)).users.source;
 					totalUsers += users.length;
 					var room:Object = new Object();
 					room.users = new Array();
-					room.name =
-						UserManager.getInstance().getConference().meetingName + " (" +
-						ResourceUtil.getInstance().getString('bbb.users.breakout.room')
-						+ " - " + (i + 1).toString() + ")";
+					room.sequence = i + 1;
+					room.name = parentMeetingName + " (" + roomResource + " - " + room.sequence.toString() + ")";
 					for (var j:int = 0; j < users.length; j++) {
 						room.users.push(users[j].userID);
 					}
@@ -105,7 +105,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 							usersInvited ||= true;
 							var event:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.REQUEST_BREAKOUT_JOIN_URL);
 							event.userId = user.userID;
-							event.breakoutId = UserManager.getInstance().getConference().internalMeetingID + "-" + (i + 1);
+							event.breakoutMeetingId = UserManager.getInstance().getConference().getBreakoutRoomBySequence(i + 1).externalMeetingId;
 							dispatcher.dispatchEvent(event);
 						}
 					}
@@ -294,8 +294,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 		</mx:HBox>
 		
 		<mx:HBox id="recordBox" width="100%" paddingTop="12">
-		    <mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.breakout.record')}" visible="false" />
-			<mx:CheckBox id="recordCheckbox" visible="false" 
+		    <mx:Label text="{ResourceUtil.getInstance().getString('bbb.users.breakout.record')}"/>
+			<mx:CheckBox id="recordCheckbox"
 						 accessibilityName="{ResourceUtil.getInstance().getString('bbb.users.breakout.recordCheckbox.accessibilityName')}"/>
 		</mx:HBox>
 		<mx:Tile id="roomsContainer" styleName="roomsContainer" width="100%" height="100%"/>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml
index bf5ccf439036e4b9227d257f0dd6d5299aefeb56..65f5cfdb93e8f91e3ccdcf12d7f2e0294e768b64 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/RoomActionsRenderer.mxml
@@ -37,14 +37,14 @@
 
 			protected function listenToBreakoutRoom(event:MouseEvent):void {
 				var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.LISTEN_IN);
-				e.breakoutId = data.breakoutId as String;
+				e.breakoutMeetingId = data.meetingId as String;
 				e.listen = listenBtn.selected;
 				globalDispatch.dispatchEvent(e);
 			}
 
 			protected function requestBreakoutJoinUrl(event:MouseEvent):void {
 				var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.REQUEST_BREAKOUT_JOIN_URL);
-				e.breakoutId = data.breakoutId as String;
+				e.breakoutMeetingId = data.externalMeetingId as String;
 				e.userId = UserManager.getInstance().getConference().getMyUserId();
 				globalDispatch.dispatchEvent(e);
 			}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
index 728d2016de8161c7cf5e3b740cec122714bad3fa..81162c71cb5885ebfd4bf8e29c4422d2e20f6860 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml
@@ -55,7 +55,6 @@
 			import mx.events.MenuEvent;
 			import mx.managers.PopUpManager;
 			
-			import org.as3commons.lang.StringUtils;
 			import org.as3commons.logging.api.ILogger;
 			import org.as3commons.logging.api.getClassLogger;
 			import org.bigbluebutton.common.IBbbModuleWindow;
@@ -343,7 +342,7 @@
 				// We display only one alert
 				removeJoinAlert();
 				joinAlert = Alert.show(
-					ResourceUtil.getInstance().getString('bbb.users.breakout.openJoinURL', [StringUtils.substringAfterLast(event.breakoutId, "-")]),
+					ResourceUtil.getInstance().getString('bbb.users.breakout.openJoinURL', [event.breakoutMeetingSequence]),
 					ResourceUtil.getInstance().getString('bbb.users.breakout.confirm'),
 					Alert.YES | Alert.NO,
 					null,
@@ -514,7 +513,7 @@
 				var br:BreakoutRoom = UserManager.getInstance().getConference().getBreakoutRoomInListen();
 				if (br != null) {
 					var e:BreakoutRoomEvent = new BreakoutRoomEvent(BreakoutRoomEvent.LISTEN_IN);
-					e.breakoutId = br.breakoutId;
+					e.breakoutMeetingId = br.meetingId;
 					e.listen = false;
 					dispatcher.dispatchEvent(e);
 				}
@@ -595,7 +594,7 @@
 			}
 			
 			private function breakoutRoomNameLabelFunction(item:Object, column:DataGridColumn) : String {
-				return ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room') + " " + StringUtils.substringAfterLast(item.breakoutId, "-");
+				return ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room') + " " + item.sequence;
 			}
 		]]>
 	</mx:Script>
@@ -633,7 +632,7 @@
 			<mx:columns>
 				<mx:DataGridColumn labelFunction="breakoutRoomNameLabelFunction" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.room')}" />
 				<mx:DataGridColumn dataField="numberOfUsers" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.users')}"/>
-				<mx:DataGridColumn dataField="breakoutId" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}"
+				<mx:DataGridColumn dataField="meetingId" headerText="{ResourceUtil.getInstance().getString('bbb.users.roomsGrid.action')}"
 								   visible="{amIModerator}"
 								   itemRenderer="org.bigbluebutton.modules.users.views.RoomActionsRenderer"/>
 			</mx:columns>
diff --git a/bigbluebutton-config/bin/bbb-record b/bigbluebutton-config/bin/bbb-record
index 6adbe521660c1729d0ef9a5d31467790a9cdb292..608707dbfeadfb9172c1dc9b84e471b0a79e2c9d 100755
--- a/bigbluebutton-config/bin/bbb-record
+++ b/bigbluebutton-config/bin/bbb-record
@@ -33,7 +33,7 @@
 #   2013-04-05 GUG  Description is optional in bbb-record --watch
 #   2013-04-05 GUG  Map internal meeting id with external meeting id
 #   2016-07-02 FFD  Updates for 1.1
-#   2016-09-27 GTR  Stricter recording directories names detection to cover breakout rooms recordings
+#   2016-10-17 GTR  Stricter rule for detection of recording directories names
 
 #set -e
 #set -x
@@ -408,7 +408,7 @@ if [ $DELETEALL ]; then
   rm -f /var/bigbluebutton/screenshare/*.flv
   rm -f /var/freeswitch/meetings/*.wav
 
-  for meeting in $(ls /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$"); do
+  for meeting in $(ls /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$"); do
     echo "deleting: $meeting"
     rm -rf /var/bigbluebutton/$meeting
   done
@@ -448,10 +448,10 @@ if [ -z $HEAD ]; then
 fi
 
 tmp_file=$(mktemp)
-ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD > $tmp_file
-ls -t /var/bigbluebutton/recording/raw | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD >> $tmp_file
+ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD > $tmp_file
+ls -t /var/bigbluebutton/recording/raw | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD >> $tmp_file
                                                  
-#for meeting in $(ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}-\?[[:digit:]]\{1\}\?$" | head -n $HEAD); do
+#for meeting in $(ls -t /var/bigbluebutton | grep "^[0-9a-f]\{40\}-[[:digit:]]\{13\}$" | head -n $HEAD); do
 for meeting in $(cat $tmp_file | sort -t - -k 2 -r| uniq); do
 	echo -n "$meeting"
 	timestamp=$(echo $meeting | sed s/.*-//g)
diff --git a/bigbluebutton-config/slides/blank-presentation.pdf b/bigbluebutton-config/slides/blank-presentation.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ebf6257403b88d9f0eeeb1dfcb096f0c9fc52a9d
Binary files /dev/null and b/bigbluebutton-config/slides/blank-presentation.pdf differ
diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle
index 67fb9d41422537d2123fe0effebe5320d40b0d70..588866d978f8b0817f50e50bda2e38bb5969a83d 100755
--- a/bigbluebutton-web/build.gradle
+++ b/bigbluebutton-web/build.gradle
@@ -19,6 +19,7 @@ dependencies {
   
 	compile 'commons-lang:commons-lang:2.5'
 	compile 'commons-io:commons-io:2.4'
+    compile 'commons-codec:commons-codec:1.10'
     compile 'com.google.code.gson:gson:1.7.1'
 	compile 'commons-httpclient:commons-httpclient:3.1'
 	compile 'com.zaxxer:nuprocess:1.1.0'
diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
index 10e12539b3b003d3f8f3552aa567b6afe0e4024f..1c9d5a130a8612393d3c55edf73eeaaef49bbfae 100755
--- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
+++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
@@ -1,220 +1,221 @@
-#
-# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
-#
-# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
-#
-# This program is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free Software
-# Foundation; either version 3.0 of the License, or (at your option) any later
-# version.
-#
-# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-#
-
-#
-# These are the default properites for BigBlueButton Web application
-
-# Default loglevel. 
-appLogLevel=DEBUG
-
-#----------------------------------------------------
-# Directory where BigBlueButton stores uploaded slides
-presentationDir=/var/bigbluebutton
-
-#----------------------------------------------------
-# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located
-swfToolsDir=/usr/bin
-
-#----------------------------------------------------
-# Directory where ImageMagick's convert executable is located
-imageMagickDir=/usr/bin
-
-#----------------------------------------------------
-# Use fullpath to ghostscript executable since the exec names are different
-# for each platform.
-ghostScriptExec=/usr/bin/gs
-
-#----------------------------------------------------
-# Fonts directory passed into PDF2SWF to support highlighting of texts
-# in the SWF slides.
-fontsDir=/usr/share/fonts
-
-#----------------------------------------------------
-# This is a workaround for a problem converting PDF files, referenced at 
-# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1
-noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps
-
-#----------------------------------------------------
-# These will be copied in cases where the conversion process
-# fails to generate a slide from the uploaded presentation
-BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf
-BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png
-
-#----------------------------------------------------
-# Number of minutes the conversion should take. If it takes
-# more than this time, cancel the conversion process.
-maxConversionTime=5
-
-#----------------------------------------------------
-# Maximum number of pages allowed for an uploaded presentation (default 100).
-maxNumPages=200
-
-#----------------------------------------------------
-# Maximum swf file size for load to the client (default 500000).
-MAX_SWF_FILE_SIZE=500000
-
-#----------------------------------------------------
-# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
-placementsThreshold=8000
-
-# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
-imageTagThreshold=8000
-
-# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500)
-defineTextThreshold=2500
-
-#------------------------------------
-# Number of threads in the pool to do the presentation conversion.
-#------------------------------------
-numConversionThreads=2
-
-#----------------------------------------------------
-# Additional conversion of the presentation slides to SVG
-# to be used in the HTML5 client
-svgImagesRequired=false
-
-# Default number of digits for voice conference users joining through the PSTN.
-defaultNumDigitsForTelVoice=5
-
-#----------------------------------------------------
-# Default dial access number
-defaultDialAccessNumber=613-555-1234
-
-#----------------------------------------------------
-# Default welcome message to display when the participant joins the web
-# conference. This is only used for the old scheduling which will be
-# removed in the future. Use the API to create a conference.
-defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner).  Use a headset to avoid causing background noise for others.<br>
-defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>.
-
-# Default maximum number of users a meeting can have.
-# Current default is 0 (meeting doesn't have a user limit).
-defaultMaxUsers=0
-
-# Default duration of the meeting in minutes.
-# Current default is 0 (meeting doesn't end).
-defaultMeetingDuration=0
-
-# Remove the meeting from memory when the end API is called.
-# This allows 3rd-party apps to recycle the meeting right-away
-# instead of waiting for the meeting to expire (see below).
-removeMeetingWhenEnded=true
-
-# The number of minutes before the system removes the meeting from memory.
-defaultMeetingExpireDuration=1
-
-# The number of minutes the system waits when a meeting is created and when
-# a user joins. If after this period, a user hasn't joined, the meeting is
-# removed from memory.
-defaultMeetingCreateJoinDuration=5
-
-# Disable recording by default. 
-#   true - don't record even if record param in the api call is set to record
-#   false - when record param is passed from api, override this default
-disableRecordingDefault=false
-
-# Start recording when first user joins the meeting.
-# For backward compatibility with 0.81 where whole meeting
-# is recorded.  
-autoStartRecording=false
-
-# Allow the user to start/stop recording.
-allowStartStopRecording=true
-
-#----------------------------------------------------
-# This URL is where the BBB client is accessible. When a user sucessfully
-# enters a name and password, she is redirected here to load the client.
-bigbluebutton.web.serverURL=http://192.168.23.44
-
-
-#----------------------------------------------------
-# Assign URL where the logged-out participant will be redirected after sign-out.
-# If "default", it returns to bigbluebutton.web.serverURL
-bigbluebutton.web.logoutURL=default
-
-# The url of the BigBlueButton client. User's will be redirected here when
-# successfully joining the meeting.
-defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html
-#defaultClientUrl=http://192.168.0.235/3rd-party.html
-
-# The default avatar image to display if nothing is passed on the JOIN API (avatarURL)
-# call. This avatar is displayed if the user isn't sharing the webcam and
-# the option (displayAvatar) is enabled in config.xml
-defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png
-
-# The URL of the default configuration
-defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml
-
-apiVersion=1.0
-
-# Salt which is used by 3rd-party apps to authenticate api calls
-securitySalt=a820d30da2db356124fce5bd5d8054b4
-
-# Directory where we drop the <meeting-id-recorded>.done file
-recordStatusDir=/var/bigbluebutton/recording/status/recorded
-
-redisHost=127.0.0.1
-redisPort=6379
-
-# The directory where the published/unpublised recordings are located. This is for
-# the get recording* api calls
-publishedDir=/var/bigbluebutton/published
-unpublishedDir=/var/bigbluebutton/unpublished
-
-# The directory where the pre-built configs are stored
-configDir=/var/bigbluebutton/configs
-
-# If the API is enabled.
-serviceEnabled = true
-
-# Test voiceBridge number
-testVoiceBridge=99999
-testConferenceMock=conference-mock-default
-
-#------------------------------------------------------
-# These properties are used to test the conversion process.
-# Conference name folder in ${presentationDir} (see above)
-beans.presentationService.testConferenceMock=${testConferenceMock}
-
-# Conference room folder in ${presentationDir}/${testConferenceMock}
-beans.presentationService.testRoomMock=conference-mock-default
-# Uploaded presentation name
-beans.presentationService.testPresentationName=appkonference
-# Uploaded presentation file
-beans.presentationService.testUploadedPresentation=appkonference.txt
-# Default Uploaded presentation file
-beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf
-
-presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation
-
-#----------------------------------------------------
-# The URL where the presentations will be loaded from.
-#----------------------------------------------------
-beans.presentationService.presentationBaseUrl=${presentationBaseURL}
-#----------------------------------------------------
-# Inject values into grails service beans
-beans.presentationService.presentationDir=${presentationDir}
-
-#----------------------------------------------------
-# Specify which IPs can do cross domain requests
-accessControlAllowOrigin=${bigbluebutton.web.serverURL}
-
-#----------------------------------------------------
-# The lapsus of seconds for polling the BBB Server in order to check if it's down.
-# After 5 tries if there isn't response, it will be declared down
-checkBBBServerEvery=10
+#
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+#
+# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
+#
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# These are the default properites for BigBlueButton Web application
+
+# Default loglevel. 
+appLogLevel=DEBUG
+
+#----------------------------------------------------
+# Directory where BigBlueButton stores uploaded slides
+presentationDir=/var/bigbluebutton
+
+#----------------------------------------------------
+# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located
+swfToolsDir=/usr/bin
+
+#----------------------------------------------------
+# Directory where ImageMagick's convert executable is located
+imageMagickDir=/usr/bin
+
+#----------------------------------------------------
+# Use fullpath to ghostscript executable since the exec names are different
+# for each platform.
+ghostScriptExec=/usr/bin/gs
+
+#----------------------------------------------------
+# Fonts directory passed into PDF2SWF to support highlighting of texts
+# in the SWF slides.
+fontsDir=/usr/share/fonts
+
+#----------------------------------------------------
+# This is a workaround for a problem converting PDF files, referenced at 
+# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1
+noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps
+
+#----------------------------------------------------
+# These will be copied in cases where the conversion process
+# fails to generate a slide from the uploaded presentation
+BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf
+BLANK_PRESENTATION=/var/bigbluebutton/blank/blank-presentation.pdf
+BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png
+
+#----------------------------------------------------
+# Number of minutes the conversion should take. If it takes
+# more than this time, cancel the conversion process.
+maxConversionTime=5
+
+#----------------------------------------------------
+# Maximum number of pages allowed for an uploaded presentation (default 100).
+maxNumPages=200
+
+#----------------------------------------------------
+# Maximum swf file size for load to the client (default 500000).
+MAX_SWF_FILE_SIZE=500000
+
+#----------------------------------------------------
+# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
+placementsThreshold=8000
+
+# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
+imageTagThreshold=8000
+
+# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500)
+defineTextThreshold=2500
+
+#------------------------------------
+# Number of threads in the pool to do the presentation conversion.
+#------------------------------------
+numConversionThreads=2
+
+#----------------------------------------------------
+# Additional conversion of the presentation slides to SVG
+# to be used in the HTML5 client
+svgImagesRequired=false
+
+# Default number of digits for voice conference users joining through the PSTN.
+defaultNumDigitsForTelVoice=5
+
+#----------------------------------------------------
+# Default dial access number
+defaultDialAccessNumber=613-555-1234
+
+#----------------------------------------------------
+# Default welcome message to display when the participant joins the web
+# conference. This is only used for the old scheduling which will be
+# removed in the future. Use the API to create a conference.
+defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner).  Use a headset to avoid causing background noise for others.<br>
+defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>.
+
+# Default maximum number of users a meeting can have.
+# Current default is 0 (meeting doesn't have a user limit).
+defaultMaxUsers=0
+
+# Default duration of the meeting in minutes.
+# Current default is 0 (meeting doesn't end).
+defaultMeetingDuration=0
+
+# Remove the meeting from memory when the end API is called.
+# This allows 3rd-party apps to recycle the meeting right-away
+# instead of waiting for the meeting to expire (see below).
+removeMeetingWhenEnded=true
+
+# The number of minutes before the system removes the meeting from memory.
+defaultMeetingExpireDuration=1
+
+# The number of minutes the system waits when a meeting is created and when
+# a user joins. If after this period, a user hasn't joined, the meeting is
+# removed from memory.
+defaultMeetingCreateJoinDuration=5
+
+# Disable recording by default. 
+#   true - don't record even if record param in the api call is set to record
+#   false - when record param is passed from api, override this default
+disableRecordingDefault=false
+
+# Start recording when first user joins the meeting.
+# For backward compatibility with 0.81 where whole meeting
+# is recorded.  
+autoStartRecording=false
+
+# Allow the user to start/stop recording.
+allowStartStopRecording=true
+
+#----------------------------------------------------
+# This URL is where the BBB client is accessible. When a user sucessfully
+# enters a name and password, she is redirected here to load the client.
+bigbluebutton.web.serverURL=http://192.168.23.44
+
+
+#----------------------------------------------------
+# Assign URL where the logged-out participant will be redirected after sign-out.
+# If "default", it returns to bigbluebutton.web.serverURL
+bigbluebutton.web.logoutURL=default
+
+# The url of the BigBlueButton client. User's will be redirected here when
+# successfully joining the meeting.
+defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html
+#defaultClientUrl=http://192.168.0.235/3rd-party.html
+
+# The default avatar image to display if nothing is passed on the JOIN API (avatarURL)
+# call. This avatar is displayed if the user isn't sharing the webcam and
+# the option (displayAvatar) is enabled in config.xml
+defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png
+
+# The URL of the default configuration
+defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml
+
+apiVersion=1.0
+
+# Salt which is used by 3rd-party apps to authenticate api calls
+securitySalt=a820d30da2db356124fce5bd5d8054b4
+
+# Directory where we drop the <meeting-id-recorded>.done file
+recordStatusDir=/var/bigbluebutton/recording/status/recorded
+
+redisHost=127.0.0.1
+redisPort=6379
+
+# The directory where the published/unpublised recordings are located. This is for
+# the get recording* api calls
+publishedDir=/var/bigbluebutton/published
+unpublishedDir=/var/bigbluebutton/unpublished
+
+# The directory where the pre-built configs are stored
+configDir=/var/bigbluebutton/configs
+
+# If the API is enabled.
+serviceEnabled = true
+
+# Test voiceBridge number
+testVoiceBridge=99999
+testConferenceMock=conference-mock-default
+
+#------------------------------------------------------
+# These properties are used to test the conversion process.
+# Conference name folder in ${presentationDir} (see above)
+beans.presentationService.testConferenceMock=${testConferenceMock}
+
+# Conference room folder in ${presentationDir}/${testConferenceMock}
+beans.presentationService.testRoomMock=conference-mock-default
+# Uploaded presentation name
+beans.presentationService.testPresentationName=appkonference
+# Uploaded presentation file
+beans.presentationService.testUploadedPresentation=appkonference.txt
+# Default Uploaded presentation file
+beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf
+
+presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation
+
+#----------------------------------------------------
+# The URL where the presentations will be loaded from.
+#----------------------------------------------------
+beans.presentationService.presentationBaseUrl=${presentationBaseURL}
+#----------------------------------------------------
+# Inject values into grails service beans
+beans.presentationService.presentationDir=${presentationDir}
+
+#----------------------------------------------------
+# Specify which IPs can do cross domain requests
+accessControlAllowOrigin=${bigbluebutton.web.serverURL}
+
+#----------------------------------------------------
+# The lapsus of seconds for polling the BBB Server in order to check if it's down.
+# After 5 tries if there isn't response, it will be declared down
+checkBBBServerEvery=10
diff --git a/bigbluebutton-web/grails-app/conf/spring/resources.xml b/bigbluebutton-web/grails-app/conf/spring/resources.xml
index 48cc70b20a40d2b2db63b7e17d4d797d98462318..4c8319a9cd832726b83ae049900f790c06e0771c 100755
--- a/bigbluebutton-web/grails-app/conf/spring/resources.xml
+++ b/bigbluebutton-web/grails-app/conf/spring/resources.xml
@@ -61,6 +61,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
     <property name="presentationBaseURL" value="${presentationBaseURL}"/>
    	<property name="pageExtractor" ref="pageExtractor"/>  
     <property name="documentConversionService" ref="documentConversionService"/>
+  	<property name="blankPresentation" value="${BLANK_PRESENTATION}"/>
   </bean>
   
   <bean id="recordingService" class="org.bigbluebutton.api.RecordingService" >
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 ef9d2b8918d83e618000b531b3ce20d992f18141..05fde7f168dfc3ddff9ee424a42b945f996c867f 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
@@ -286,19 +286,9 @@ class ApiController {
       return
     }
 
-    Boolean isBreakoutRoom = false
-    if(!StringUtils.isEmpty(params.isBreakout)) {
-      isBreakoutRoom = new Boolean(StringUtils.strip(params.isBreakout))
-    }
-
     // Everything is good so far. Translate the external meeting id to an internal meeting id. If
     // we can't find the meeting, complain.
     String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingId);
-    if (isBreakoutRoom) {
-      // This is a join request for a breakout room. Use the passed meetingId to find the meeting.
-      internalMeetingId = externalMeetingId
-      log.info("Join request for breakout room " + internalMeetingId)
-    }
 
     log.info("Retrieving meeting ${internalMeetingId}")
     Meeting meeting = meetingService.getMeeting(internalMeetingId);
@@ -869,6 +859,10 @@ class ApiController {
                   meeting {
                     meetingID() { mkp.yield(m.getExternalId()) }
                     internalMeetingID() { mkp.yield(m.getInternalId()) }
+                    if (m.isBreakout()) {
+                        parentMeetingID() { mkp.yield(m.getParentMeetingId()) }
+                        sequence(m.getSequence())
+                    }
                     isBreakout() { mkp.yield(m.isBreakout()) }
                     meetingName() { mkp.yield(m.getName()) }
                     createTime(m.getCreateTime())
@@ -2136,6 +2130,10 @@ class ApiController {
             isBreakout() { mkp.yield(meeting.isBreakout()) }
             meetingID() { mkp.yield(meeting.getExternalId()) }
             internalMeetingID(meeting.getInternalId())
+            if (m.isBreakout()) {
+                parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) }
+                sequence(meeting.getSequence())
+            }
             createTime(meeting.getCreateTime())
             createDate(formatPrettyDate(meeting.getCreateTime()))
             voiceBridge() { mkp.yield(meeting.getTelVoice()) }
@@ -2196,6 +2194,7 @@ class ApiController {
             returncode(RESP_CODE_SUCCESS)
             meetingID() { mkp.yield(meeting.getExternalId()) }
             internalMeetingID() { mkp.yield(meeting.getInternalId()) }
+            parentMeetingID() { mkp.yield(meeting.getParentMeetingId()) }
             attendeePW() { mkp.yield(meeting.getViewerPassword()) }
             moderatorPW() { mkp.yield(meeting.getModeratorPassword()) }
             createTime(meeting.getCreateTime())
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java
index c06f635980917d19fcf9d7b3c0b916817c0a1d54..f4d59fb037174815e96acd7d050b4021e4645107 100644
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ClientConfigService.java
@@ -19,9 +19,6 @@
 
 package org.bigbluebutton.api;
 
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java
index afbb19b22aa8b0c56615e962f1ce09e75c7ea8c0..1f3249cd3e2389d7af96bed40472248d26f1626a 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java
@@ -301,15 +301,27 @@ public class MeetingService implements MessageListener {
             // TODO: Need a better way to store these values for recordings
             metadata.put("meetingId", m.getExternalId());
             metadata.put("meetingName", m.getName());
+            metadata.put("isBreakout", m.isBreakout().toString());
 
-            messagingService.recordMeetingInfo(m.getInternalId(), metadata);
+            Map<String, String> breakoutMetadata = new TreeMap<String, String>();
+            if (m.isBreakout() != null){
+                breakoutMetadata.put("sequence", m.getSequence().toString());
+                breakoutMetadata.put("parentMeetingId", m.getParentMeetingId());
+            }
+
+            messagingService.recordMeetingInfo(m.getInternalId(), metadata, breakoutMetadata);
         }
 
         Map<String, Object> logData = new HashMap<String, Object>();
         logData.put("meetingId", m.getInternalId());
         logData.put("externalMeetingId", m.getExternalId());
+        if (m.isBreakout() != null){
+            logData.put("sequence", m.getSequence());
+            logData.put("parentMeetingId", m.getParentMeetingId());
+        }
         logData.put("name", m.getName());
         logData.put("duration", m.getDuration());
+        logData.put("isBreakout", m.isBreakout());
         logData.put("record", m.isRecord());
         logData.put("event", "create_meeting");
         logData.put("description", "Create meeting.");
@@ -320,11 +332,11 @@ public class MeetingService implements MessageListener {
         log.info("Create meeting: data={}", logStr);
 
         messagingService.createMeeting(m.getInternalId(), m.getExternalId(),
-                m.getName(), m.isRecord(), m.getTelVoice(), m.getDuration(),
-                m.getAutoStartRecording(), m.getAllowStartStopRecording(),
-                m.getModeratorPassword(), m.getViewerPassword(),
-                m.getCreateTime(), formatPrettyDate(m.getCreateTime()),
-                m.isBreakout());
+                m.getParentMeetingId(), m.getName(), m.isRecord(),
+                m.getTelVoice(), m.getDuration(), m.getAutoStartRecording(),
+                m.getAllowStartStopRecording(), m.getModeratorPassword(),
+                m.getViewerPassword(), m.getCreateTime(),
+                formatPrettyDate(m.getCreateTime()), m.isBreakout(), m.getSequence());
     }
 
     private String formatPrettyDate(Long timestamp) {
@@ -359,15 +371,9 @@ public class MeetingService implements MessageListener {
     public Meeting getMeeting(String meetingId) {
         if (meetingId == null)
             return null;
-        int dashes = meetingId.split("-", -1).length - 1;
         for (String key : meetings.keySet()) {
-            int keyDashes = key.split("-", -1).length - 1;
-            if (dashes == 2
-                    && key.equals(meetingId)
-                    || (dashes < 2 && keyDashes < 2 && key
-                            .startsWith(meetingId))) {
+            if (key.startsWith(meetingId))
                 return (Meeting) meetings.get(key);
-            }
         }
 
         return null;
@@ -515,20 +521,21 @@ public class MeetingService implements MessageListener {
     }
 
     private void processCreateBreakoutRoom(CreateBreakoutRoom message) {
-        Meeting parentMeeting = getMeeting(message.parentId);
+        Meeting parentMeeting = getMeeting(message.parentMeetingId);
         if (parentMeeting != null) {
 
             Map<String, String> params = new HashMap<String, String>();
             params.put("name", message.name);
-            params.put("breakoutId", message.breakoutId);
-            params.put("meetingID", message.parentId);
+            params.put("meetingID", message.meetingId);
+            params.put("parentMeetingID", message.parentMeetingId);
             params.put("isBreakout", "true");
+            params.put("sequence", message.sequence.toString());
             params.put("attendeePW", message.viewerPassword);
             params.put("moderatorPW", message.moderatorPassword);
             params.put("voiceBridge", message.voiceConfId);
             params.put("duration", message.durationInMinutes.toString());
             params.put("record", message.record.toString());
-            params.put("welcome", getMeeting(message.parentId)
+            params.put("welcome", getMeeting(message.parentMeetingId)
                     .getWelcomeMessageTemplate());
 
             Map<String, String> parentMeetingMetadata = parentMeeting
@@ -545,17 +552,18 @@ public class MeetingService implements MessageListener {
 
             handleCreateMeeting(breakout);
 
-            presDownloadService.extractPage(message.parentId,
+            presDownloadService.extractPage(message.parentMeetingId,
                     message.sourcePresentationId,
                     message.sourcePresentationSlide, breakout.getInternalId());
         } else {
-            log.error("Failed to create breakout room " + message.breakoutId
-                    + ".Reason: Parent meeting not found.");
+            log.error(
+                    "Failed to create breakout room {}.Reason: Parent meeting {} not found.",
+                    message.meetingId, message.parentMeetingId);
         }
     }
 
     private void processEndBreakoutRoom(EndBreakoutRoom message) {
-        processEndMeeting(new EndMeeting(message.breakoutId));
+        processEndMeeting(new EndMeeting(message.breakoutMeetingId));
     }
 
     private void processEndMeeting(EndMeeting message) {
@@ -591,6 +599,9 @@ public class MeetingService implements MessageListener {
                 Map<String, Object> logData = new HashMap<String, Object>();
                 logData.put("meetingId", m.getInternalId());
                 logData.put("externalMeetingId", m.getExternalId());
+                if (m.isBreakout()) {
+                    logData.put("parentMeetingId", m.getParentMeetingId());
+                }
                 logData.put("name", m.getName());
                 logData.put("duration", m.getDuration());
                 logData.put("record", m.isRecord());
@@ -601,11 +612,14 @@ public class MeetingService implements MessageListener {
                 Gson gson = new Gson();
                 String logStr = gson.toJson(logData);
 
-                log.info("Meeting restarted: data={}", logStr);
+                log.info("Meeting started: data={}", logStr);
             } else {
                 Map<String, Object> logData = new HashMap<String, Object>();
                 logData.put("meetingId", m.getInternalId());
                 logData.put("externalMeetingId", m.getExternalId());
+                if (m.isBreakout()) {
+                    logData.put("parentMeetingId", m.getParentMeetingId());
+                }
                 logData.put("name", m.getName());
                 logData.put("duration", m.getDuration());
                 logData.put("record", m.isRecord());
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java
index 37517ec569d345f64babe564b795a1786e61fe63..be29faa84fef1c75d9840b5546172c82ddb7e683 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java
@@ -288,84 +288,108 @@ public class ParamsProcessorUtil {
     return metas;
 	}
 	
-	public Meeting processCreateParams(Map<String, String> params) {
-	    String meetingName = params.get("name");
-	    if(meetingName == null){
-	    	meetingName = "";
-	    }
-	    String externalMeetingId = params.get("meetingID");
-	    
-	    String viewerPass = processPassword(params.get("attendeePW"));
-	    String modPass = processPassword(params.get("moderatorPW")); 
-	    
-	    // Get the digits for voice conference for users joining through the phone.
-	    // If none is provided, generate one.
-	    String telVoice = processTelVoice(params.get("voiceBridge"));
-	    
-	    // Get the voice conference digits/chars for users joing through VOIP on the client.
-	    // If none is provided, make it the same as the telVoice. If one has been provided,
-	    // we expect that the users will be joined in the same voice conference.
-	    String webVoice = params.get("webVoice");
-	    if (StringUtils.isEmpty(webVoice)) {
-	      webVoice = telVoice;
-	    }
-	    
-	    // Get all the other relevant parameters and generate defaults if none has been provided.
-	    String dialNumber = processDialNumber(params.get("dialNumber"));
-	    String logoutUrl = processLogoutUrl(params.get("logoutURL")); 
-	    boolean record = processRecordMeeting(params.get("record"));
-	    int maxUsers = processMaxUser(params.get("maxParticipants"));
-	    int meetingDuration = processMeetingDuration(params.get("duration"));
-	    
+    public Meeting processCreateParams(Map<String, String> params) {
+        String meetingName = params.get("name");
+        if (meetingName == null) {
+            meetingName = "";
+        }
+        String externalMeetingId = params.get("meetingID");
+
+        String viewerPass = processPassword(params.get("attendeePW"));
+        String modPass = processPassword(params.get("moderatorPW"));
+
+        // Get the digits for voice conference for users joining through the
+        // phone.
+        // If none is provided, generate one.
+        String telVoice = processTelVoice(params.get("voiceBridge"));
+
+        // Get the voice conference digits/chars for users joing through VOIP on
+        // the client.
+        // If none is provided, make it the same as the telVoice. If one has
+        // been provided,
+        // we expect that the users will be joined in the same voice conference.
+        String webVoice = params.get("webVoice");
+        if (StringUtils.isEmpty(webVoice)) {
+            webVoice = telVoice;
+        }
+
+        // Get all the other relevant parameters and generate defaults if none
+        // has been provided.
+        String dialNumber = processDialNumber(params.get("dialNumber"));
+        String logoutUrl = processLogoutUrl(params.get("logoutURL"));
+        boolean record = processRecordMeeting(params.get("record"));
+        int maxUsers = processMaxUser(params.get("maxParticipants"));
+        int meetingDuration = processMeetingDuration(params.get("duration"));
+
         // set is breakout room property
         boolean isBreakout = false;
         if (!StringUtils.isEmpty(params.get("isBreakout"))) {
             isBreakout = new Boolean(params.get("isBreakout"));
         }
 
-        String welcomeMessageTemplate = processWelcomeMessage(params.get("welcome"), isBreakout);
-        String welcomeMessage = substituteKeywords(welcomeMessageTemplate, dialNumber, telVoice, meetingName);
-	      
-	    String internalMeetingId = convertToInternalMeetingId(externalMeetingId);
-	    
-	    // Check if this is a test meeting. NOTE: This should not belong here. Extract this out.				
-	    if (isTestMeeting(telVoice)) {
-	      internalMeetingId = getIntMeetingIdForTestMeeting(telVoice);
-	    }
-	   
-	    boolean autoStartRec = autoStartRecording;
-	    if (!StringUtils.isEmpty(params.get("autoStartRecording"))) {
-				try {
-					autoStartRec = Boolean.parseBoolean(params.get("autoStartRecording"));
-				} catch(Exception ex){ 
-					log.warn("Invalid param [autoStartRecording] for meeting=[" + internalMeetingId + "]");
-				}
-	    }
+        String welcomeMessageTemplate = processWelcomeMessage(
+                params.get("welcome"), isBreakout);
+        String welcomeMessage = substituteKeywords(welcomeMessageTemplate,
+                dialNumber, telVoice, meetingName);
 
-	    boolean allowStartStoptRec = allowStartStopRecording;
-	    if (!StringUtils.isEmpty(params.get("allowStartStopRecording"))) {
-				try {
-					allowStartStoptRec = Boolean.parseBoolean(params.get("allowStartStopRecording"));
-				} catch(Exception ex){ 
-					log.warn("Invalid param [allowStartStopRecording] for meeting=[" + internalMeetingId + "]");
-				}
-	    }
-	    
-	    // Collect metadata for this meeting that the third-party app wants to store if meeting is recorded.
-	    Map<String, String> meetingInfo = new HashMap<String, String>();
-	    meetingInfo = processMetaParam(params);
-	    	    
-	    // Create a unique internal id by appending the current time. This way, the 3rd-party
-	    // app can reuse the external meeting id.
-	    long createTime = System.currentTimeMillis();
-	    internalMeetingId = internalMeetingId + '-' + new Long(createTime).toString();
+        String internalMeetingId = convertToInternalMeetingId(externalMeetingId);
+
+        // Check if this is a test meeting. NOTE: This should not belong here.
+        // Extract this out.
+        if (isTestMeeting(telVoice)) {
+            internalMeetingId = getIntMeetingIdForTestMeeting(telVoice);
+        }
+
+        boolean autoStartRec = autoStartRecording;
+        if (!StringUtils.isEmpty(params.get("autoStartRecording"))) {
+            try {
+                autoStartRec = Boolean.parseBoolean(params
+                        .get("autoStartRecording"));
+            } catch (Exception ex) {
+                log.warn("Invalid param [autoStartRecording] for meeting=[{}]",
+                        internalMeetingId);
+            }
+        }
+
+        boolean allowStartStoptRec = allowStartStopRecording;
+        if (!StringUtils.isEmpty(params.get("allowStartStopRecording"))) {
+            try {
+                allowStartStoptRec = Boolean.parseBoolean(params
+                        .get("allowStartStopRecording"));
+            } catch (Exception ex) {
+                log.warn(
+                        "Invalid param [allowStartStopRecording] for meeting=[{}]",
+                        internalMeetingId);
+            }
+        }
+
+        // Collect metadata for this meeting that the third-party app wants to
+        // store if meeting is recorded.
+        Map<String, String> meetingInfo = new HashMap<String, String>();
+        meetingInfo = processMetaParam(params);
+
+        // Create a unique internal id by appending the current time. This way,
+        // the 3rd-party
+        // app can reuse the external meeting id.
+        long createTime = System.currentTimeMillis();
+        internalMeetingId = internalMeetingId.concat("-").concat(
+                new Long(createTime).toString());
 
         // If this create meeting request is for a breakout room, we just used
-        // the passed in breakoutId as the internal meetingId so we can
-        // correlate
-        // the breakout meeting with it's parent meeting.
+        // we need to generate a unique internal and external id and keep
+        // tracks of the parent meeting id
+        String parentMeetingId = new String();
         if (isBreakout) {
-            internalMeetingId = params.get("breakoutId");
+            internalMeetingId = params.get("meetingID");
+            parentMeetingId = params.get("parentMeetingID");
+            // We rebuild the the external meeting using the has of the parent
+            // meeting, the shared timestamp and the sequence number
+            String timeStamp = StringUtils.substringAfter(internalMeetingId,
+                    "-");
+            String externalHash = DigestUtils.shaHex(parentMeetingId
+                    .concat("-").concat(timeStamp.toString()).concat("-")
+                    .concat(params.get("sequence")));
+            externalMeetingId = externalHash.concat("-").concat(timeStamp);
         }
 
         // Create the meeting with all passed in parameters.
@@ -392,8 +416,14 @@ public class ParamsProcessorUtil {
             meeting.setModeratorOnlyMessage(moderatorOnlyMessage);
         }
 
+        // Add extra parameters for breakout room
+        if (isBreakout) {
+            meeting.setSequence(Integer.parseInt(params.get("sequence")));
+            meeting.setParentMeetingId(parentMeetingId);
+        }
+
         return meeting;
-	}
+    }
 	
 	public String getApiVersion() {
 		return apiVersion;
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java
index 6e2d568fa53c50f2368badb2f947f84db35d6f0f..892d69f33fa9154fad43050868dadef34c494d40 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java
@@ -21,11 +21,11 @@ package org.bigbluebutton.api.domain;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+
 import org.apache.commons.lang.RandomStringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,7 +37,9 @@ public class Meeting {
 	
 	private String name;
 	private String extMeetingId;
-	private String intMeetingId;	
+	private String intMeetingId;
+	private String parentMeetingId;
+	private Integer sequence = 0;
 	private Integer duration = 0;	 
 	private long createdTime = 0;
 	private long startTime = 0;
@@ -152,7 +154,15 @@ public class Meeting {
 	public long getCreateTime() {
 		return createdTime;
 	}
-	
+
+	public Integer setSequence(Integer s) {
+        return sequence = s;
+    }
+
+	public Integer getSequence() {
+        return sequence;
+    }
+
 	public Integer getDuration() {
 		return duration;
 	}
@@ -201,6 +211,14 @@ public class Meeting {
 		return intMeetingId;
 	}
 
+	public String setParentMeetingId(String p) {
+        return parentMeetingId = p;
+    }
+
+	public String getParentMeetingId() {
+	    return parentMeetingId;
+	}
+
 	public String getWebVoice() {
 		return webVoice;
 	}
@@ -375,7 +393,7 @@ public class Meeting {
 	public static class Builder {
     	private String name;
     	private String externalId;
-    	private String internalId;   	
+    	private String internalId;
     	private int maxUsers;
     	private boolean record;
     	private boolean autoStartRecording;
@@ -393,18 +411,18 @@ public class Meeting {
     	private String defaultAvatarURL;
     	private long createdTime;
     	private boolean isBreakout;
-    	
+
     	public Builder(String externalId, String internalId, long createTime) {
     		this.externalId = externalId;
     		this.internalId = internalId;
     		this.createdTime = createTime;
     	}
-    	
+ 
     	public Builder withName(String name) {
     		this.name = name;
     		return this;
     	}
-    	    	
+
     	public Builder withDuration(int minutes) {
     		duration = minutes;
     		return this;
@@ -474,7 +492,7 @@ public class Meeting {
     	  isBreakout = b;
     	  return this;
     	}
-    	   	
+
     	public Builder withLogoutUrl(String l) {
     		logoutUrl = l;
     		return this;
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
index 4c67e5cee9a86d2e224676f4660c018b8b27744b..9bfd168087ef0de3c6559bed909a0e07cc3ed8fc 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
@@ -72,13 +72,14 @@ public class MeetingMessageHandler implements MessageHandler {
             CreateBreakoutRoomRequest msg = new Gson().fromJson(message, CreateBreakoutRoomRequest.class);
             for (MessageListener listener : listeners) {
               listener.handle(new CreateBreakoutRoom(
-                  msg.payload.breakoutId, 
-                  msg.payload.parentId,
-                  msg.payload.name, 
-                  msg.payload.voiceConfId, 
-                  msg.payload.viewerPassword, 
-                  msg.payload.moderatorPassword, 
-                  msg.payload.durationInMinutes, 
+                  msg.payload.breakoutMeetingId,
+                  msg.payload.parentMeetingId,
+                  msg.payload.name,
+                  msg.payload.sequence,
+                  msg.payload.voiceConfId,
+                  msg.payload.viewerPassword,
+                  msg.payload.moderatorPassword,
+                  msg.payload.durationInMinutes,
                   msg.payload.sourcePresentationId,
                   msg.payload.sourcePresentationSlide,
                   msg.payload.record
@@ -88,7 +89,7 @@ public class MeetingMessageHandler implements MessageHandler {
           }
           else if (EndBreakoutRoomRequest.NAME.equals(messageName)) {
             EndBreakoutRoomRequest msg = new Gson().fromJson(message, EndBreakoutRoomRequest.class);
-            log.info("Received an end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId);
+            log.info("Received end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId);
             for (MessageListener listener : listeners) {
               listener.handle(new EndBreakoutRoom(msg.payload.meetingId));
             }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java
index 9be59010d49be788273742f53a77c1eed5221ab7..5b62209ea9b085cfe6241cd09870a64775c8b039 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessageReceiver.java
@@ -7,7 +7,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
 import redis.clients.jedis.JedisPubSub;
 import redis.clients.jedis.exceptions.JedisConnectionException;
 
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java
index 19f8ef24dbf348ac69c40bccf4adfc6bf719f9eb..b665c9b3984642d28a63d84d3070d5ea48e99779 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java
@@ -27,12 +27,14 @@ import java.util.Map;
 import java.util.Set;
 
 public interface MessagingService {	
-	void recordMeetingInfo(String meetingId, Map<String, String> info);
+	void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo);
 	void destroyMeeting(String meetingID);
-	void createMeeting(String meetingID, String externalMeetingID, String meetingName, Boolean recorded, 
-			      String voiceBridge, Integer duration, Boolean autoStartRecording,
-			      Boolean allowStartStopRecording, String moderatorPass, String viewerPass,
-			      Long createTime, String createDate, Boolean isBreakout);
+    void createMeeting(String meetingID, String externalMeetingID,
+            String parentMeetingID, String meetingName, Boolean recorded,
+            String voiceBridge, Integer duration, Boolean autoStartRecording,
+            Boolean allowStartStopRecording, String moderatorPass,
+            String viewerPass, Long createTime, String createDate,
+            Boolean isBreakout, Integer sequence);
 	void endMeeting(String meetingId);
 	void send(String channel, String message);
 	void sendPolls(String meetingId, String title, String question, String questionType, List<String> answers);
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java
index 206df68b158ae6bad11593b913744996ed7b09a7..4d4d4e75d66b700e74c449d5a3382446f2ab1303 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java
@@ -19,28 +19,27 @@
 
 package org.bigbluebutton.api.messaging;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.*;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import javax.imageio.ImageIO;
+import java.util.Set;
+
 import org.bigbluebutton.api.messaging.converters.messages.DestroyMeetingMessage;
 import org.bigbluebutton.api.messaging.converters.messages.EndMeetingMessage;
 import org.bigbluebutton.api.messaging.converters.messages.RegisterUserMessage;
 import org.bigbluebutton.common.converters.ToJsonEncoder;
+import org.bigbluebutton.common.messages.Constants;
 import org.bigbluebutton.common.messages.MessagingConstants;
+import org.bigbluebutton.common.messages.SendStunTurnInfoReplyMessage;
 import org.bigbluebutton.messages.CreateMeetingRequest;
 import org.bigbluebutton.messages.CreateMeetingRequest.CreateMeetingRequestPayload;
-import org.bigbluebutton.common.messages.Constants;
-import org.bigbluebutton.common.messages.PubSubPingMessage;
-import org.bigbluebutton.common.messages.payload.PubSubPingMessagePayload;
-import org.bigbluebutton.common.messages.SendStunTurnInfoReplyMessage;
 import org.bigbluebutton.web.services.turn.StunServer;
 import org.bigbluebutton.web.services.turn.TurnEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import com.google.gson.Gson;
 
 public class RedisMessagingService implements MessagingService {
@@ -50,8 +49,8 @@ public class RedisMessagingService implements MessagingService {
 	private MessageSender sender;
 	private ToJsonEncoder encoder = new ToJsonEncoder();
 	
-	public void recordMeetingInfo(String meetingId, Map<String, String> info) {
-		storeService.recordMeetingInfo(meetingId, info);
+	public void recordMeetingInfo(String meetingId, Map<String, String> info, Map<String, String> breakoutInfo) {
+		storeService.recordMeetingInfo(meetingId, info, breakoutInfo);
 	}
 
 	public void destroyMeeting(String meetingID) {
@@ -68,22 +67,24 @@ public class RedisMessagingService implements MessagingService {
 		sender.send(MessagingConstants.TO_MEETING_CHANNEL, json);		
 	}
 	
-	public void createMeeting(String meetingID, String externalMeetingID, String meetingName, Boolean recorded, 
-			                      String voiceBridge, Integer duration, 
-			                      Boolean autoStartRecording, Boolean allowStartStopRecording,
-			                      String moderatorPass, String viewerPass, Long createTime,
-			                      String createDate, Boolean isBreakout) {
-	  CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload(meetingID, externalMeetingID, meetingName, 
-				                                  recorded, voiceBridge, duration, 
-				                                  autoStartRecording, allowStartStopRecording,
-				                                  moderatorPass, viewerPass, createTime, createDate, isBreakout);
-	  CreateMeetingRequest msg = new CreateMeetingRequest(payload);
-	  
-	  Gson gson = new Gson();
-		String json = gson.toJson(msg);
-		log.info("Sending create meeting message to bbb-apps:[{}]", json);
-		sender.send(MessagingConstants.TO_MEETING_CHANNEL, json);			
-	}
+    public void createMeeting(String meetingID, String externalMeetingID,
+            String parentMeetingID, String meetingName, Boolean recorded,
+            String voiceBridge, Integer duration, Boolean autoStartRecording,
+            Boolean allowStartStopRecording, String moderatorPass,
+            String viewerPass, Long createTime, String createDate,
+            Boolean isBreakout, Integer sequence) {
+        CreateMeetingRequestPayload payload = new CreateMeetingRequestPayload(
+                meetingID, externalMeetingID, parentMeetingID, meetingName,
+                recorded, voiceBridge, duration, autoStartRecording,
+                allowStartStopRecording, moderatorPass, viewerPass, createTime,
+                createDate, isBreakout, sequence);
+        CreateMeetingRequest msg = new CreateMeetingRequest(payload);
+
+        Gson gson = new Gson();
+        String json = gson.toJson(msg);
+        log.info("Sending create meeting message to bbb-apps:[{}]", json);
+        sender.send(MessagingConstants.TO_MEETING_CHANNEL, json);
+    }
 	
 	public void endMeeting(String meetingId) {
 		EndMeetingMessage msg = new EndMeetingMessage(meetingId);
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java
index 1797a97ff2cb8cca950915e8d0c10b3b954deb3f..f27a34d9b928dfee9f95f22d1e0935f1de55b33d 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java
@@ -28,24 +28,32 @@ public class RedisStorageService {
 		// CLIENT LIST on redis-cli
 		redisPool = new JedisPool(new GenericObjectPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, null,
 		        Protocol.DEFAULT_DATABASE, "BbbRed5AppsPub");
-					
 	}
 	
-	public void recordMeetingInfo(String meetingId, Map<String, String> info) {
-		Jedis jedis = redisPool.getResource();
-		try {
-		    for (String key: info.keySet()) {
-				   log.debug("Storing metadata {} = {}", key, info.get(key));
-				}   
+    public void recordMeetingInfo(String meetingId, Map<String, String> info,
+            Map<String, String> breakoutInfo) {
+        Jedis jedis = redisPool.getResource();
+        try {
+            for (String key : info.keySet()) {
+                log.debug("Storing metadata {} = {}", key, info.get(key));
+            }
 
-		    log.debug("Saving metadata in {}", meetingId);
-			jedis.hmset("meeting:info:" + meetingId, info);
-		} catch (Exception e){
-			log.warn("Cannot record the info meeting:"+meetingId,e);
-		} finally {
-			jedis.close();
-		}		
-	}
+            log.debug("Saving metadata in {}", meetingId);
+            jedis.hmset("meeting:info:" + meetingId, info);
+
+            for (String breakoutKey : breakoutInfo.keySet()) {
+                log.debug("Storing breakout metadata {} = {}", breakoutKey,
+                        breakoutInfo.get(breakoutKey));
+            }
+
+            log.debug("Saving breakout metadata in {}", meetingId);
+            jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo);
+        } catch (Exception e) {
+            log.warn("Cannot record the info meeting:" + meetingId, e);
+        } finally {
+            jedis.close();
+        }
+    }
 	
 	public void removeMeeting(String meetingId){
 		Jedis jedis = redisPool.getResource();
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java
index 1ef60574382534f96b596635a2458610d82773f7..79aedee164ff5c5b7b117f8a738c731396028d3e 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java
@@ -2,9 +2,10 @@ package org.bigbluebutton.api.messaging.messages;
 
 public class CreateBreakoutRoom implements IMessage {
 
-    public final String breakoutId;
-    public final String parentId; // The main meeting internal id
+    public final String meetingId;
+    public final String parentMeetingId; // The main meeting internal id
     public final String name; // The name of the breakout room
+    public final Integer sequence; // The sequence number of the breakout room
     public final String voiceConfId; // The voice conference id
     public final String viewerPassword;
     public final String moderatorPassword;
@@ -13,14 +14,15 @@ public class CreateBreakoutRoom implements IMessage {
     public final Integer sourcePresentationSlide;
     public final Boolean record;
 
-    public CreateBreakoutRoom(String breakoutId, String parentId, String name,
-            String voiceConfId, String viewerPassword,
-            String moderatorPassword, Integer duration,
+    public CreateBreakoutRoom(String meetingId, String parentMeetingId,
+            String name, Integer sequence, String voiceConfId,
+            String viewerPassword, String moderatorPassword, Integer duration,
             String sourcePresentationId, Integer sourcePresentationSlide,
             Boolean record) {
-        this.breakoutId = breakoutId;
-        this.parentId = parentId;
+        this.meetingId = meetingId;
+        this.parentMeetingId = parentMeetingId;
         this.name = name;
+        this.sequence = sequence;
         this.voiceConfId = voiceConfId;
         this.viewerPassword = viewerPassword;
         this.moderatorPassword = moderatorPassword;
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java
index dc5c2b36832fe51f70ff02da06ca4eac054282c2..c996f31de9285a042fdfa537d4179b53892aa5a9 100644
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/messages/EndBreakoutRoom.java
@@ -1,9 +1,9 @@
 package org.bigbluebutton.api.messaging.messages;
 
 public class EndBreakoutRoom implements IMessage {
-  public final String breakoutId;
+  public final String breakoutMeetingId;
 
-  public EndBreakoutRoom(String breakoutId) {
-    this.breakoutId = breakoutId;
+  public EndBreakoutRoom(String breakoutMeetingId) {
+    this.breakoutMeetingId = breakoutMeetingId;
   }
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
index f28a59b3163175bc49475c84066a28a02eda3906..63836e91e40be3ad1b2b9cee6b33a5dd9361b16f 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
@@ -27,6 +27,7 @@ public class PresentationUrlDownloadService {
     private DocumentConversionService documentConversionService;
     private String presentationBaseURL;
     private String presentationDir;
+    private String BLANK_PRESENTATION;
 
     public void processUploadedPresentation(UploadedPresentation uploadedPres) {
         documentConversionService.processDocument(uploadedPres);
@@ -43,11 +44,12 @@ public class PresentationUrlDownloadService {
     public void extractPage(String sourceMeetingId, String presentationId,
             Integer presentationSlide, String destinationMeetingId) {
 
-        // Construct the source meeting path
+        // Build the source meeting path
         File sourceMeetingPath = new File(presentationDir + File.separator
                 + sourceMeetingId + File.separator + sourceMeetingId
                 + File.separator + presentationId);
 
+        // Find the source meeting presentation file
         final String presentationFilter = presentationId;
         FilenameFilter pdfFilter = new FilenameFilter() {
             public boolean accept(File dir, String name) {
@@ -57,7 +59,7 @@ public class PresentationUrlDownloadService {
         };
 
         File[] matches = sourceMeetingPath.listFiles(pdfFilter);
-        if (matches.length != 1) {
+        if (matches != null && matches.length != 1) {
             // No PDF presentation was found, we look for an image presentation
             FilenameFilter imgFlter = new FilenameFilter() {
                 public boolean accept(File dir, String name) {
@@ -67,40 +69,44 @@ public class PresentationUrlDownloadService {
 
             matches = sourceMeetingPath.listFiles(imgFlter);
         }
-        if (matches.length != 1) {
-            log.info("Not matching PDF file with prefix {} found at {}",
+        File sourcePresentationFile;
+        if (matches == null || matches.length != 1) {
+            log.warn(
+                    "Not matching PDF file with prefix {} found at {}. Using the default blank PDF",
                     sourceMeetingId, sourceMeetingPath);
+            sourcePresentationFile = new File(BLANK_PRESENTATION);
         } else {
-            File sourcePresentationFile = matches[0];
-            String filenameExt = FilenameUtils
-                    .getExtension(sourcePresentationFile.getName());
-            String presId = generatePresentationId(presentationId);
-            String newFilename = Util.createNewFilename(presId, filenameExt);
-
-            File uploadDir = createPresentationDirectory(destinationMeetingId,
-                    presentationDir, presId);
-            String newFilePath = uploadDir.getAbsolutePath()
-                    + File.separatorChar + newFilename;
-            File newPresentation = new File(newFilePath);
-
-            if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) {
-                pageExtractor.extractPage(sourcePresentationFile, new File(
-                        newFilePath), presentationSlide);
-            } else {
-                try {
-                    FileUtils.copyFile(sourcePresentationFile, newPresentation);
-                } catch (IOException e) {
-                    log.error("Could not copy presentation {} to {}",
-                            sourcePresentationFile.getAbsolutePath(),
-                            newPresentation.getAbsolutePath());
-                    e.printStackTrace();
-                }
+            sourcePresentationFile = matches[0];
+        }
+        // Build the target meeting path
+        String filenameExt = FilenameUtils.getExtension(sourcePresentationFile
+                .getName());
+        String presId = generatePresentationId(presentationId);
+        String newFilename = Util.createNewFilename(presId, filenameExt);
+
+        File uploadDir = createPresentationDirectory(destinationMeetingId,
+                presentationDir, presId);
+        String newFilePath = uploadDir.getAbsolutePath() + File.separatorChar
+                + newFilename;
+        File newPresentation = new File(newFilePath);
+
+        if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) {
+            pageExtractor.extractPage(sourcePresentationFile, new File(
+                    newFilePath), presentationSlide);
+        } else {
+            try {
+                FileUtils.copyFile(sourcePresentationFile, newPresentation);
+            } catch (IOException e) {
+                log.error("Could not copy presentation {} to {}",
+                        sourcePresentationFile.getAbsolutePath(),
+                        newPresentation.getAbsolutePath());
+                e.printStackTrace();
             }
-
-            processUploadedFile(destinationMeetingId, presId, "default-"
-                    + presentationSlide.toString() + "." + filenameExt,
-                    newPresentation);
         }
+
+        processUploadedFile(destinationMeetingId, presId, "default-"
+                + presentationSlide.toString() + "." + filenameExt,
+                newPresentation);
     }
 
     public String generatePresentationId(String name) {
@@ -220,4 +226,8 @@ public class PresentationUrlDownloadService {
         this.documentConversionService = documentConversionService;
     }
 
+    public void setBlankPresentation(String blankPresentation) {
+        this.BLANK_PRESENTATION = blankPresentation;
+    }
+
 }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java
index 74fc9385c29ea2d4e533e7d1c140b2b299577245..2e917caf12651af408f60dcff4ab6e3fbc734153 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/Pdf2SwfPageCounter.java
@@ -21,7 +21,6 @@ package org.bigbluebutton.presentation.imp;
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -29,7 +28,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.bigbluebutton.presentation.PageCounter;
-import org.bigbluebutton.presentation.imp.ExternalProcessExecutor.InterruptTimerTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java
index a4e3369bd04e2b39a2e6806bb1c912a4cbf9ada2..929dcfb259a9746b1157806d74803f9527257f11 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java
@@ -30,7 +30,6 @@ import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
index 3b57b9e295d14d356c8d9dbdb5682c86c5d6fb4f..f9ee00f0ab8c4b205cd52ef5d828e8be6613f1d3 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
@@ -1,13 +1,9 @@
 package org.bigbluebutton.presentation.imp;
 
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
 
-import org.bigbluebutton.presentation.SvgImageCreator;
 import org.bigbluebutton.presentation.SupportedFileTypes;
+import org.bigbluebutton.presentation.SvgImageCreator;
 import org.bigbluebutton.presentation.UploadedPresentation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java
index 1fda818a94ec4d7345f36c6d62067d2e8a5467c6..c85d6695660bced734f81bf2ed753ff81d09de43 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/imp/SwfSlidesGenerationProgressNotifier.java
@@ -21,14 +21,13 @@ package org.bigbluebutton.presentation.imp;
 
 import java.util.Map;
 
-import org.apache.commons.lang.StringEscapeUtils;
 import org.bigbluebutton.api.messaging.MessagingConstants;
 import org.bigbluebutton.api.messaging.MessagingService;
 import org.bigbluebutton.presentation.ConversionMessageConstants;
 import org.bigbluebutton.presentation.ConversionUpdateMessage;
+import org.bigbluebutton.presentation.ConversionUpdateMessage.MessageBuilder;
 import org.bigbluebutton.presentation.GeneratedSlidesInfoHelper;
 import org.bigbluebutton.presentation.UploadedPresentation;
-import org.bigbluebutton.presentation.ConversionUpdateMessage.MessageBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java b/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java
index d0ee4734b5aeb17e29e394d020add7c2ec058eb4..87a07fffacc516eb1813e005d5535ad9264ac363 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/web/services/KeepAliveService.java
@@ -19,20 +19,6 @@
 
 package org.bigbluebutton.web.services;
 
-import org.bigbluebutton.api.messaging.MessageListener;
-import org.bigbluebutton.api.messaging.MessagingService;
-import org.bigbluebutton.api.messaging.MessagingConstants;
-import org.bigbluebutton.api.messaging.RedisMessagingService;
-import org.bigbluebutton.api.messaging.messages.IMessage;
-import org.bigbluebutton.api.messaging.messages.KeepAliveReply;
-import org.bigbluebutton.api.messaging.messages.MeetingDestroyed;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -40,7 +26,12 @@ import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
-import com.google.gson.Gson;
+import org.bigbluebutton.api.messaging.MessageListener;
+import org.bigbluebutton.api.messaging.MessagingService;
+import org.bigbluebutton.api.messaging.messages.IMessage;
+import org.bigbluebutton.api.messaging.messages.KeepAliveReply;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class KeepAliveService implements MessageListener {
 	private static Logger log = LoggerFactory.getLogger(KeepAliveService.class);