diff --git a/akka-bbb-apps/build.sbt b/akka-bbb-apps/build.sbt index b5474a81bb..b3328562cf 100755 --- a/akka-bbb-apps/build.sbt +++ b/akka-bbb-apps/build.sbt @@ -50,7 +50,7 @@ libraryDependencies ++= { "com.google.code.gson" % "gson" % "1.7.1", "redis.clients" % "jedis" % "2.1.0", "org.apache.commons" % "commons-lang3" % "3.2", - "org.bigbluebutton" % "bbb-common-message" % "0.0.4" + "org.bigbluebutton" % "bbb-common-message" % "0.0.5" )} diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java index f72392105a..d7c0b3aafc 100755 --- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java +++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java @@ -17,6 +17,11 @@ public interface IBigBlueButtonInGW { void getAllMeetings(String meetingID); void lockSettings(String meetingID, Boolean locked, Map lockSettigs); + // Polling + void votePoll(String meetingId, String userId, String pollId, Integer questionId, Integer answerId); + void startPoll(String meetingId, String requesterId, String pollId, String pollType); + void stopPoll(String meetingId, String userId, String pollId); + void showPollResult(String meetingId, String requesterId, String pollId, Boolean show); // Lock void initLockSettings(String meetingID, Map settings); diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PollingMessageReceiver.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PollingMessageReceiver.java new file mode 100755 index 0000000000..ba4f4f80ae --- /dev/null +++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/PollingMessageReceiver.java @@ -0,0 +1,44 @@ +package org.bigbluebutton.core.pubsub.receivers; + +import org.bigbluebutton.common.messages.*; + +import com.google.gson.JsonParser; +import com.google.gson.JsonObject; + +import org.bigbluebutton.core.api.IBigBlueButtonInGW; + +public class PollingMessageReceiver implements MessageHandler{ + + private IBigBlueButtonInGW bbbGW; + + public PollingMessageReceiver(IBigBlueButtonInGW bbbGW) { + this.bbbGW = bbbGW; + } + + @Override + public void handleMessage(String pattern, String channel, String message) { + if (channel.equalsIgnoreCase(MessagingConstants.TO_POLLING_CHANNEL)) { + JsonParser parser = new JsonParser(); + JsonObject obj = (JsonObject) parser.parse(message); + if (obj.has("header") && obj.has("payload")) { + JsonObject header = (JsonObject) obj.get("header"); + if (header.has("name")) { + String messageName = header.get("name").getAsString(); + if (VotePollUserRequestMessage.VOTE_POLL_REQUEST.equals(messageName)) { + VotePollUserRequestMessage msg = VotePollUserRequestMessage.fromJson(message); + bbbGW.votePoll(msg.meetingId, msg.userId, msg.pollId, msg.questionId, msg.answerId); + } else if (StartPollRequestMessage.START_POLL_REQUEST.equals(messageName)){ + StartPollRequestMessage msg = StartPollRequestMessage.fromJson(message); + bbbGW.startPoll(msg.meetingId, msg.requesterId, msg.pollId, msg.pollType); + } else if (StopPollRequestMessage.STOP_POLL_REQUEST.equals(messageName)){ + StopPollRequestMessage msg = StopPollRequestMessage.fromJson(message); + bbbGW.stopPoll(msg.meetingId, msg.requesterId, msg.pollId); + } else if (ShowPollResultRequestMessage.SHOW_POLL_RESULT_REQUEST.equals(messageName)){ + ShowPollResultRequestMessage msg = ShowPollResultRequestMessage.fromJson(message); + bbbGW.showPollResult(msg.meetingId, msg.requesterId, msg.pollId, msg.show); + } + } + } + } + } +} diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/RedisMessageReceiver.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/RedisMessageReceiver.java index 0e6eeb5c4b..3c932c0e4d 100755 --- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/RedisMessageReceiver.java +++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/pubsub/receivers/RedisMessageReceiver.java @@ -31,6 +31,9 @@ public class RedisMessageReceiver { WhiteboardMessageReceiver whiteboardRx = new WhiteboardMessageReceiver(bbbGW); receivers.add(whiteboardRx); + PollingMessageReceiver pollRx = new PollingMessageReceiver(bbbGW); + receivers.add(pollRx); + MeetingMessageReceiver meetingRx = new MeetingMessageReceiver(bbbGW); receivers.add(meetingRx); } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala index 30955b7101..081ded6075 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala @@ -46,6 +46,7 @@ object Boot extends App with SystemConfiguration { val presSender = new PresentationEventRedisPublisher(msgSender) val userSender = new UsersEventRedisPublisher(msgSender) val whiteboardSender = new WhiteboardEventRedisPublisher(msgSender) + val pollingSender = new PollingEventRedisPublisher(msgSender) val outMessageListeners = new java.util.ArrayList[OutMessageListener2]() outMessageListeners.add(chatSender) @@ -57,6 +58,7 @@ object Boot extends App with SystemConfiguration { outMessageListeners.add(presentationEventRecorder) outMessageListeners.add(usersEventRecorder) outMessageListeners.add(whiteboardEventRecorder) + outMessageListeners.add(pollingSender) val outGW = new MessageOutGateway(outMessageListeners) val bbbInGW = new BigBlueButtonInGW(system, outGW, voiceEventRecorder) 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 a924b63322..766711b344 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 @@ -432,4 +432,25 @@ class BigBlueButtonInGW(val system: ActorSystem, outGW: MessageOutGateway, voice def voiceRecording(voiceConfId: String, recordingFile: String, timestamp: String, recording: java.lang.Boolean) { bbbActor ! new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp) } + + // Polling + def votePoll(meetingId: String, userId: String, pollId: String, questionId: Integer, answerId: Integer) { + bbbActor ! new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId) + } + + def startPoll(meetingId: String, requesterId: String, pollId: String, pollType: String) { + bbbActor ! new StartPollRequest(meetingId, requesterId, pollId, pollType) + } + + def stopPoll(meetingId: String, userId: String, pollId: String) { + bbbActor ! new StopPollRequest(meetingId, userId, pollId) + } + + def showPollResult(meetingId: String, requesterId: String, pollId: String, show: java.lang.Boolean) { + if (show) { + bbbActor ! new ShowPollResultRequest(meetingId, requesterId, pollId) + } else { + bbbActor ! new HidePollResultRequest(meetingId, requesterId, pollId) + } + } } 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 c3ae721a85..265c989187 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 @@ -117,7 +117,7 @@ case class PollShowResultMessage(meetingID: String, recorded: Boolean, requester case class ShowPollResultReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage case class PollHideResultMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String) extends IOutMessage case class HidePollResultReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage -case class UserRespondedToPollMessage(meetingID: String, recorded: Boolean, requesterId: String, pollId: String, poll: SimplePollResultOutVO) extends IOutMessage +case class UserRespondedToPollMessage(meetingID: String, recorded: Boolean, presenterId: String, pollId: String, poll: SimplePollResultOutVO) extends IOutMessage case class RespondToPollReplyMessage(meetingID: String, recorded: Boolean, result: RequestResult, requesterId: String, pollId: String) extends IOutMessage case class GetCurrentPollReplyMessage(meetingID: String, recorded: Boolean, requesterId: String, hasPoll: Boolean, poll: Option[PollVO]) extends IOutMessage diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/Poll.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/Poll.scala index 1d3b355a2e..f0df6862f4 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/Poll.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/Poll.scala @@ -113,8 +113,10 @@ case class Responder(val userId: String, name: String) case class ResponseOutVO(id: String, text: String, responders: Array[Responder] = Array[Responder]()) case class QuestionOutVO(id: String, multiResponse: Boolean, question: String, responses: Array[ResponseOutVO]) + case class SimpleAnswerOutVO(id: Int, key: String) case class SimplePollOutVO(id: String, answers: Array[SimpleAnswerOutVO]) + case class SimpleVoteOutVO(id: Int, key: String, numVotes: Int) case class SimplePollResultOutVO(id: String, answers: Array[SimpleVoteOutVO]) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala index ba93bc5296..907d1314e5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/PollApp.scala @@ -127,7 +127,10 @@ trait PollApp { case Some(user) => { val responder = new Responder(user.userID, user.name) pollModel.respondToQuestion(poll.id, msg.questionId, msg.answerId, responder) - outGW.send(new UserRespondedToPollMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll)) + users.getCurrentPresenter foreach { cp => + outGW.send(new UserRespondedToPollMessage(mProps.meetingID, mProps.recorded, cp.userID, msg.pollId, poll)) + } + } case None => { val result = new RequestResult(StatusCodes.FORBIDDEN, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND))) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/PollingEventRedisPublisher.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/PollingEventRedisPublisher.scala new file mode 100755 index 0000000000..3c88afc278 --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/PollingEventRedisPublisher.scala @@ -0,0 +1,126 @@ +package org.bigbluebutton.core.pubsub.senders + +import org.bigbluebutton.core.api.OutMessageListener2 +import org.bigbluebutton.core.MessageSender +import org.bigbluebutton.core.api._ +import com.google.gson.Gson +import scala.collection.mutable.HashMap +import collection.JavaConverters._ +import scala.collection.JavaConversions._ +import java.util.ArrayList +import org.bigbluebutton.common.messages.MessagingConstants +import org.bigbluebutton.core.messaging.Util +import org.bigbluebutton.core.apps.SimplePollOutVO +import org.bigbluebutton.core.apps.SimplePollResultOutVO + +class PollingEventRedisPublisher(service: MessageSender) extends OutMessageListener2 { + def handleMessage(msg: IOutMessage) { + msg match { + case msg: PollStartedMessage => handlePollStartedMessage(msg) + case msg: PollStoppedMessage => handlePollStoppedMessage(msg) + case msg: PollShowResultMessage => handlePollShowResultMessage(msg) + case msg: PollHideResultMessage => handlePollHideResultMessage(msg) + case msg: UserRespondedToPollMessage => handleUserRespondedToPollMessage(msg) + case _ => // do nothing + } + } + + private def handlePollStartedMessage(msg: PollStartedMessage) { + val json = pollStartedMessageToJson(msg) + service.send(MessagingConstants.FROM_POLLING_CHANNEL, json) + } + + private def handlePollStoppedMessage(msg: PollStoppedMessage) { + val json = pollStoppedMessageToJson(msg) + service.send(MessagingConstants.FROM_POLLING_CHANNEL, json) + } + + private def handlePollShowResultMessage(msg: PollShowResultMessage) { + val json = pollShowResultMessageToJson(msg) + service.send(MessagingConstants.FROM_POLLING_CHANNEL, json) + } + + private def handlePollHideResultMessage(msg: PollHideResultMessage) { + val json = pollHideResultMessageToJson(msg) + service.send(MessagingConstants.FROM_POLLING_CHANNEL, json) + } + + private def handleUserRespondedToPollMessage(msg: UserRespondedToPollMessage) { + // val json = ChatMessageToJsonConverter.sendPrivateMessageEventToJson(msg) + // service.send(MessagingConstants.FROM_CHAT_CHANNEL, json) + } + + private def pollVOtoMap(msg: SimplePollOutVO): java.util.HashMap[String, Object] = { + val pollVO = new java.util.HashMap[String, Object]() + pollVO.put("id", msg.id) + + val answers = new java.util.ArrayList[java.util.Map[String, Any]]; + msg.answers.foreach(ans => { + val amap = new java.util.HashMap[String, Any]() + amap.put("id", ans.id) + amap.put("key", ans.key) + answers.add(amap) + }) + + pollVO.put("answers", answers) + + pollVO + } + + private def pollStartedMessageToJson(msg: PollStartedMessage): String = { + // val payload = new java.util.HashMap[String, Any]() + // payload.put(Constants.MEETING_ID, msg.meetingID) + // payload.put(org.bigbluebutton.common.messages.PollStartedMessage.REQUESTER_ID, msg.requesterId) + + // val pollVO = pollVOtoMap(msg.poll) + // payload.put(org.bigbluebutton.common.messages.PollStartedMessage.POLL, pollVO) + + // val header = Util.buildHeader(org.bigbluebutton.common.messages.PollStartedMessage.POLL_STARTED, None) + // Util.buildJson(header, payload) + + val pollVO = pollVOtoMap(msg.poll) + val psm = new org.bigbluebutton.common.messages.PollStartedMessage(msg.meetingID, msg.requesterId, pollVO) + psm.toJson + } + + private def pollStoppedMessageToJson(msg: PollStoppedMessage): String = { + val psm = new org.bigbluebutton.common.messages.PollStoppedMessage(msg.meetingID, msg.requesterId, msg.pollId) + psm.toJson + } + + private def pollResultVOtoMap(msg: SimplePollResultOutVO): java.util.HashMap[String, Object] = { + val pollVO = new java.util.HashMap[String, Object]() + pollVO.put("id", msg.id) + + val answers = new java.util.ArrayList[java.util.Map[String, Any]]; + msg.answers.foreach(ans => { + val amap = new java.util.HashMap[String, Any]() + amap.put("id", ans.id) + amap.put("key", ans.key) + amap.put("num_votes", ans.numVotes) + answers.add(amap) + }) + + pollVO.put("answers", answers) + + pollVO + } + + private def pollShowResultMessageToJson(msg: PollShowResultMessage): String = { + val pollResultVO = pollResultVOtoMap(msg.poll) + + val psm = new org.bigbluebutton.common.messages.PollShowResultMessage(msg.meetingID, pollResultVO) + psm.toJson + } + + private def pollHideResultMessageToJson(msg: PollHideResultMessage): String = { + val psm = new org.bigbluebutton.common.messages.PollHideResultMessage(msg.meetingID, msg.pollId) + psm.toJson + } + + private def UserRespondedToPollMessageTpJson(msg: UserRespondedToPollMessage): String = { + val pollResultVO = pollResultVOtoMap(msg.poll) + val psm = new org.bigbluebutton.common.messages.UserVotedPollMessage(msg.meetingID, msg.presenterId, pollResultVO) + psm.toJson + } +} \ No newline at end of file diff --git a/akka-bbb-fsesl/build.sbt b/akka-bbb-fsesl/build.sbt index 81333704a3..d55a4f4864 100755 --- a/akka-bbb-fsesl/build.sbt +++ b/akka-bbb-fsesl/build.sbt @@ -50,7 +50,7 @@ libraryDependencies ++= { "com.google.code.gson" % "gson" % "1.7.1", "redis.clients" % "jedis" % "2.1.0", "org.apache.commons" % "commons-lang3" % "3.2", - "org.bigbluebutton" % "bbb-common-message" % "0.0.4", + "org.bigbluebutton" % "bbb-common-message" % "0.0.5", "org.bigbluebutton" % "bbb-fsesl-client" % "0.0.2" )} diff --git a/bbb-common-message/build.sbt b/bbb-common-message/build.sbt index 911d8b1a5e..804867129e 100755 --- a/bbb-common-message/build.sbt +++ b/bbb-common-message/build.sbt @@ -4,7 +4,7 @@ name := "bbb-common-message" organization := "org.bigbluebutton" -version := "0.0.5-SNAPSHOT" +version := "0.0.5" // We want to have our jar files in lib_managed dir. // This way we'll have the right path when we import @@ -40,11 +40,16 @@ crossPaths := false // This forbids including Scala related libraries into the dependency autoScalaLibrary := false +/*************************** +* When developing, change the version above to x.x.x-SNAPSHOT then use the file resolver to +* publish to the local maven repo using "sbt publish" +*/ +// Uncomment this to publish to local maven repo while commenting out the nexus repo //publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository"))) -publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/dev/repo/maven-repo/releases" )) ) - +// Comment this out when publishing to local maven repo using SNAPSHOT version. +// To push to sonatype "sbt publishSigned" publishTo := { val nexus = "https://oss.sonatype.org/" if (isSnapshot.value) diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollHideResultMessage.java b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollHideResultMessage.java new file mode 100755 index 0000000000..5114a71970 --- /dev/null +++ b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollHideResultMessage.java @@ -0,0 +1,59 @@ +package org.bigbluebutton.common.messages; + +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + + +public class PollHideResultMessage implements ISubscribedMessage { + public static final String POLL_HIDE_RESULT = "poll_hide_result_message"; + public static final String VERSION = "0.0.1"; + + public static final String MEETING_ID = "meeting_id"; + public static final String POLL_ID = "poll_id"; + + public final String meetingId; + public final String pollId; + + public PollHideResultMessage(String meetingId, String pollId) { + this.meetingId = meetingId; + this.pollId = pollId; + } + + public String toJson() { + HashMap payload = new HashMap(); + payload.put(MEETING_ID, meetingId); + payload.put(POLL_ID, pollId); + + java.util.HashMap header = MessageBuilder.buildHeader(POLL_HIDE_RESULT, VERSION, null); + + return MessageBuilder.buildJson(header, payload); + } + + public static PollHideResultMessage fromJson(String message) { + JsonParser parser = new JsonParser(); + JsonObject obj = (JsonObject) parser.parse(message); + + if (obj.has("header") && obj.has("payload")) { + JsonObject header = (JsonObject) obj.get("header"); + JsonObject payload = (JsonObject) obj.get("payload"); + + if (header.has("name")) { + String messageName = header.get("name").getAsString(); + if (POLL_HIDE_RESULT.equals(messageName)) { + if (payload.has(Constants.MEETING_ID) + && payload.has(POLL_ID)) { + String id = payload.get(Constants.MEETING_ID).getAsString(); + String pollId = payload.get(POLL_ID).getAsString(); + + return new PollHideResultMessage(id, pollId); + } + } + } + } + return null; + + } +} diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollStoppedMessage.java b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollStoppedMessage.java new file mode 100755 index 0000000000..ea6b76a520 --- /dev/null +++ b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/PollStoppedMessage.java @@ -0,0 +1,63 @@ +package org.bigbluebutton.common.messages; + +import java.util.HashMap; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + + +public class PollStoppedMessage implements ISubscribedMessage { + public static final String POLL_STOPPED = "poll_stopped_message"; + public static final String VERSION = "0.0.1"; + + public static final String MEETING_ID = "meeting_id"; + public static final String REQUESTER_ID = "requester_id"; + public static final String POLL_ID = "poll_id"; + + public final String meetingId; + public final String requesterId; + public final String pollId; + + public PollStoppedMessage(String meetingId, String requesterId, String pollId) { + this.meetingId = meetingId; + this.requesterId = requesterId; + this.pollId = pollId; + } + + public String toJson() { + HashMap payload = new HashMap(); + payload.put(MEETING_ID, meetingId); + payload.put(REQUESTER_ID, requesterId); + payload.put(POLL_ID, pollId); + + java.util.HashMap header = MessageBuilder.buildHeader(POLL_STOPPED, VERSION, null); + + return MessageBuilder.buildJson(header, payload); + } + + public static PollStoppedMessage fromJson(String message) { + JsonParser parser = new JsonParser(); + JsonObject obj = (JsonObject) parser.parse(message); + + if (obj.has("header") && obj.has("payload")) { + JsonObject header = (JsonObject) obj.get("header"); + JsonObject payload = (JsonObject) obj.get("payload"); + + if (header.has("name")) { + String messageName = header.get("name").getAsString(); + if (POLL_STOPPED.equals(messageName)) { + if (payload.has(Constants.MEETING_ID) + && payload.has(REQUESTER_ID) + && payload.has(POLL_ID)) { + String id = payload.get(Constants.MEETING_ID).getAsString(); + String requesterId = payload.get(REQUESTER_ID).getAsString(); + String pollId = payload.get(POLL_ID).getAsString(); + + return new PollStoppedMessage(id, requesterId, pollId); + } + } + } + } + return null; + + } +} diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/VotePollUserRequestMessage.java b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/VotePollUserRequestMessage.java new file mode 100755 index 0000000000..79f52d7718 --- /dev/null +++ b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/VotePollUserRequestMessage.java @@ -0,0 +1,75 @@ +package org.bigbluebutton.common.messages; + +import java.util.HashMap; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + + +public class VotePollUserRequestMessage implements ISubscribedMessage { + public static final String VOTE_POLL_REQUEST = "vote_poll_user_request_message"; + public static final String VERSION = "0.0.1"; + + public static final String MEETING_ID = "meeting_id"; + public static final String USER_ID = "user_id"; + public static final String POLL_ID = "poll_id"; + public static final String QUESTION_ID = "question_id"; + public static final String ANSWER_ID = "answer_id"; + + public final String meetingId; + public final String userId; + public final String pollId; + public final Integer questionId; + public final Integer answerId; + + public VotePollUserRequestMessage(String meetingId, String userId, String pollId, Integer questionId, Integer answerId) { + this.meetingId = meetingId; + this.userId = userId; + this.pollId = pollId; + this.questionId = questionId; + this.answerId = answerId; + } + + public String toJson() { + HashMap payload = new HashMap(); + payload.put(MEETING_ID, meetingId); + payload.put(USER_ID, userId); + payload.put(POLL_ID, pollId); + payload.put(QUESTION_ID, questionId); + payload.put(ANSWER_ID, answerId); + + java.util.HashMap header = MessageBuilder.buildHeader(VOTE_POLL_REQUEST, VERSION, null); + + return MessageBuilder.buildJson(header, payload); + } + + public static VotePollUserRequestMessage fromJson(String message) { + JsonParser parser = new JsonParser(); + JsonObject obj = (JsonObject) parser.parse(message); + + if (obj.has("header") && obj.has("payload")) { + JsonObject header = (JsonObject) obj.get("header"); + JsonObject payload = (JsonObject) obj.get("payload"); + + if (header.has("name")) { + String messageName = header.get("name").getAsString(); + if (VOTE_POLL_REQUEST.equals(messageName)) { + if (payload.has(Constants.MEETING_ID) + && payload.has(USER_ID) + && payload.has(POLL_ID) + && payload.has(QUESTION_ID) + && payload.has(ANSWER_ID)) { + String id = payload.get(Constants.MEETING_ID).getAsString(); + String userId = payload.get(USER_ID).getAsString(); + String pollId = payload.get(POLL_ID).getAsString(); + Integer questionId = payload.get(QUESTION_ID).getAsInt(); + Integer answerId = payload.get(ANSWER_ID).getAsInt(); + + return new VotePollUserRequestMessage(id, userId, pollId, questionId, answerId); + } + } + } + } + return null; + + } +} diff --git a/bigbluebutton-apps/build.gradle b/bigbluebutton-apps/build.gradle index c85089a28f..72478ee971 100755 --- a/bigbluebutton-apps/build.gradle +++ b/bigbluebutton-apps/build.gradle @@ -23,7 +23,7 @@ repositories { //url "${System.properties['user.home']}/.ivy2/local" url "/home/ubuntu/.ivy2/local" layout "pattern", { - artifact "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" + artifact "[organisation]/[module]/[revision]/[artifact].[ext]" m2compatible = true } } @@ -41,7 +41,7 @@ repositories { name = "blindside-repos" addArtifactPattern "http://blindside.googlecode.com/svn/repository/[artifact](-[revision]).[ext]" addArtifactPattern "http://blindside.googlecode.com/svn/repository/[organisation]/[artifact](-[revision]).[ext]" - } + } add(new org.apache.ivy.plugins.resolver.URLResolver()) { name = "testng_ibiblio_maven2" m2compatible = true @@ -112,7 +112,7 @@ dependencies { compile 'com.google.code.gson:gson:1.7.1' providedCompile 'org.apache.commons:commons-lang3:3.2' - compile 'org.bigbluebutton:bbb-common-message:0.0.4' + compile 'org.bigbluebutton:bbb-common-message:0.0.5' } test { diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/PollingClientMessageSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/PollingClientMessageSender.java new file mode 100755 index 0000000000..9932eecb1f --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/PollingClientMessageSender.java @@ -0,0 +1,108 @@ +package org.bigbluebutton.red5.client; + + +import java.util.HashMap; +import java.util.Map; +import org.bigbluebutton.common.messages.*; +import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage; +import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService; +import org.bigbluebutton.red5.client.messaging.DirectClientMessage; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class PollingClientMessageSender { + private ConnectionInvokerService service; + + public PollingClientMessageSender(ConnectionInvokerService service) { + this.service = service; + } + + public void handlePollMessage(String message) { + JsonParser parser = new JsonParser(); + JsonObject obj = (JsonObject) parser.parse(message); + + if (obj.has("header") && obj.has("payload")) { + JsonObject header = (JsonObject) obj.get("header"); + + if (header.has("name")) { + String messageName = header.get("name").getAsString(); + + switch (messageName) { + case PollStartedMessage.POLL_STARTED: + processPollStartedMessage(message); + break; + case PollStoppedMessage.POLL_STOPPED: + processPollStoppedMessage(message); + break; + case PollShowResultMessage.POLL_SHOW_RESULT: + processPollShowResultMessage(message); + break; + case UserVotedPollMessage.USER_VOTED_POLL: + processUserVotedPollMessage(message); + break; + } + } + } + } + + private void processPollStartedMessage(String json) { + PollStartedMessage msg = PollStartedMessage.fromJson(json); + if (msg != null) { + Map args = new HashMap(); + args.put("poll", msg.poll); + + Map message = new HashMap(); + Gson gson = new Gson(); + message.put("msg", gson.toJson(args)); + + BroadcastClientMessage b = new BroadcastClientMessage(msg.meetingId, "pollStartedMessage", message); + service.sendMessage(b); + } + } + + private void processPollStoppedMessage(String json) { + PollStoppedMessage msg = PollStoppedMessage.fromJson(json); + if (msg != null) { + Map args = new HashMap(); + args.put("pollId", msg.pollId); + + Map message = new HashMap(); + Gson gson = new Gson(); + message.put("msg", gson.toJson(args)); + + BroadcastClientMessage b = new BroadcastClientMessage(msg.meetingId, "pollStoppedMessage", message); + service.sendMessage(b); + } + } + + private void processPollShowResultMessage(String json) { + PollShowResultMessage msg = PollShowResultMessage.fromJson(json); + if (msg != null) { + Map args = new HashMap(); + args.put("poll", msg.poll); + + Map message = new HashMap(); + Gson gson = new Gson(); + message.put("msg", gson.toJson(args)); + + BroadcastClientMessage b = new BroadcastClientMessage(msg.meetingId, "pollShowResultMessage", message); + service.sendMessage(b); + } + } + + private void processUserVotedPollMessage(String json) { + UserVotedPollMessage msg = UserVotedPollMessage.fromJson(json); + if (msg != null) { + Map args = new HashMap(); + args.put("poll", msg.poll); + + Map message = new HashMap(); + Gson gson = new Gson(); + message.put("msg", gson.toJson(args)); + + DirectClientMessage b = new DirectClientMessage(msg.meetingId, msg.presenterId, "pollUserVotedMessage", message); + service.sendMessage(b); + } + } +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java index e312611e03..4d9d731b80 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java @@ -1,52 +1,8 @@ package org.bigbluebutton.red5.pubsub; import java.util.Map; -import org.bigbluebutton.common.messages.AssignPresenterRequestMessage; -import org.bigbluebutton.common.messages.BroadcastLayoutRequestMessage; -import org.bigbluebutton.common.messages.ClearWhiteboardRequestMessage; -import org.bigbluebutton.common.messages.EjectUserFromMeetingRequestMessage; -import org.bigbluebutton.common.messages.EjectUserFromVoiceRequestMessage; -import org.bigbluebutton.common.messages.EnableWhiteboardRequestMessage; -import org.bigbluebutton.common.messages.GetChatHistoryRequestMessage; -import org.bigbluebutton.common.messages.GetCurrentLayoutRequestMessage; -import org.bigbluebutton.common.messages.GetLockSettingsMessage; -import org.bigbluebutton.common.messages.GetPresentationInfoMessage; -import org.bigbluebutton.common.messages.GetRecordingStatusRequestMessage; -import org.bigbluebutton.common.messages.GetSlideInfoMessage; -import org.bigbluebutton.common.messages.GetUsersRequestMessage; -import org.bigbluebutton.common.messages.GoToSlideMessage; -import org.bigbluebutton.common.messages.InitAudioSettingsMessage; -import org.bigbluebutton.common.messages.InitPermissionsSettingMessage; -import org.bigbluebutton.common.messages.IsMeetingMutedRequestMessage; -import org.bigbluebutton.common.messages.IsWhiteboardEnabledRequestMessage; -import org.bigbluebutton.common.messages.LockLayoutRequestMessage; -import org.bigbluebutton.common.messages.LockMuteUserRequestMessage; -import org.bigbluebutton.common.messages.LockUserMessage; -import org.bigbluebutton.common.messages.MessagingConstants; -import org.bigbluebutton.common.messages.MuteAllExceptPresenterRequestMessage; -import org.bigbluebutton.common.messages.MuteAllRequestMessage; -import org.bigbluebutton.common.messages.MuteUserRequestMessage; -import org.bigbluebutton.common.messages.RemovePresentationMessage; -import org.bigbluebutton.common.messages.RequestWhiteboardAnnotationHistoryRequestMessage; -import org.bigbluebutton.common.messages.ResizeAndMoveSlideMessage; -import org.bigbluebutton.common.messages.SendConversionCompletedMessage; -import org.bigbluebutton.common.messages.SendConversionUpdateMessage; -import org.bigbluebutton.common.messages.SendCursorUpdateMessage; -import org.bigbluebutton.common.messages.SendLockSettingsMessage; -import org.bigbluebutton.common.messages.SendPageCountErrorMessage; -import org.bigbluebutton.common.messages.SendPrivateChatMessage; -import org.bigbluebutton.common.messages.SendPublicChatMessage; -import org.bigbluebutton.common.messages.SendSlideGeneratedMessage; -import org.bigbluebutton.common.messages.SendWhiteboardAnnotationRequestMessage; -import org.bigbluebutton.common.messages.SetRecordingStatusRequestMessage; -import org.bigbluebutton.common.messages.SetUserStatusRequestMessage; -import org.bigbluebutton.common.messages.SharePresentationMessage; -import org.bigbluebutton.common.messages.UserLeavingMessage; -import org.bigbluebutton.common.messages.UserLoweredHandMessage; -import org.bigbluebutton.common.messages.UserRaisedHandMessage; -import org.bigbluebutton.common.messages.UserShareWebcamRequestMessage; -import org.bigbluebutton.common.messages.UserUnshareWebcamRequestMessage; -import org.bigbluebutton.common.messages.ValidateAuthTokenMessage; + +import org.bigbluebutton.common.messages.*; import org.bigbluebutton.red5.pubsub.redis.MessageSender; public class MessagePublisher { @@ -57,6 +13,27 @@ public class MessagePublisher { this.sender = sender; } + // Polling + public void votePoll(String meetingId, String userId, String pollId, Integer questionId, Integer answerId) { + VotePollUserRequestMessage msg = new VotePollUserRequestMessage(meetingId, userId, pollId, questionId, answerId); + sender.send(MessagingConstants.TO_POLLING_CHANNEL, msg.toJson()); + } + + public void startPoll(String meetingId, String requesterId, String pollId, String pollType) { + StartPollRequestMessage msg = new StartPollRequestMessage(meetingId, requesterId, pollId, pollType); + sender.send(MessagingConstants.TO_POLLING_CHANNEL, msg.toJson()); + } + + public void stopPoll(String meetingId, String userId, String pollId) { + StopPollRequestMessage msg = new StopPollRequestMessage(meetingId, userId, pollId); + sender.send(MessagingConstants.TO_POLLING_CHANNEL, msg.toJson()); + } + + public void showPollResult(String meetingId, String requesterId, String pollId, Boolean show) { + ShowPollResultRequestMessage msg = new ShowPollResultRequestMessage(meetingId, requesterId, pollId, show); + sender.send(MessagingConstants.TO_POLLING_CHANNEL, msg.toJson()); + } + public void initLockSettings(String meetingID, Map permissions) { InitPermissionsSettingMessage msg = new InitPermissionsSettingMessage(meetingID, permissions); sender.send(MessagingConstants.TO_USERS_CHANNEL, msg.toJson()); diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/redis/RedisPubSubMessageHandler.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/redis/RedisPubSubMessageHandler.java index 3fa88efc26..e78a0a152d 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/redis/RedisPubSubMessageHandler.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/pubsub/redis/RedisPubSubMessageHandler.java @@ -2,6 +2,7 @@ package org.bigbluebutton.red5.pubsub.redis; import org.bigbluebutton.common.messages.MessagingConstants; import org.bigbluebutton.red5.client.MeetingClientMessageSender; +import org.bigbluebutton.red5.client.PollingClientMessageSender; import org.bigbluebutton.red5.client.PresentationClientMessageSender; import org.bigbluebutton.red5.client.UserClientMessageSender; import org.bigbluebutton.red5.client.ChatClientMessageSender; @@ -18,7 +19,7 @@ public class RedisPubSubMessageHandler implements MessageHandler { private PresentationClientMessageSender presentationMessageSender; private WhiteboardClientMessageSender whiteboardMessageSender; private BbbAppsIsKeepAliveHandler bbbAppsIsKeepAliveHandler; - + private PollingClientMessageSender pollingMessageSender; public void setConnectionInvokerService(ConnectionInvokerService s) { this.service = s; @@ -27,6 +28,7 @@ public class RedisPubSubMessageHandler implements MessageHandler { chatMessageSender = new ChatClientMessageSender(service); presentationMessageSender = new PresentationClientMessageSender(service); whiteboardMessageSender = new WhiteboardClientMessageSender(service); + pollingMessageSender = new PollingClientMessageSender(service); } public void setBbbAppsIsKeepAliveHandler(BbbAppsIsKeepAliveHandler handler) { @@ -47,6 +49,8 @@ public class RedisPubSubMessageHandler implements MessageHandler { whiteboardMessageSender.handleWhiteboardMessage(message); } else if (channel.equalsIgnoreCase(MessagingConstants.BBB_APPS_KEEP_ALIVE_CHANNEL)) { bbbAppsIsKeepAliveHandler.handleKeepAliveMessage(message); + } else if (channel.equalsIgnoreCase(MessagingConstants.FROM_POLLING_CHANNEL)) { + pollingMessageSender.handlePollMessage(message); } } diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/PollingService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/PollingService.java new file mode 100755 index 0000000000..de5dafcf87 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/service/PollingService.java @@ -0,0 +1,107 @@ +/** +* 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 . +* Author: Felipe Cecagno +*/ +package org.bigbluebutton.red5.service; + +import java.util.Map; + +import org.bigbluebutton.red5.BigBlueButtonSession; +import org.bigbluebutton.red5.Constants; +import org.bigbluebutton.red5.pubsub.MessagePublisher; +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.Red5; +import org.slf4j.Logger; + +public class PollingService { + + private static Logger log = Red5LoggerFactory.getLogger( PollingService.class, "bigbluebutton" ); + + private MessagePublisher red5GW; + + public void setRed5Publisher(MessagePublisher inGW) { + red5GW = inGW; + } + + public void votePoll(Map message) { + log.debug("Received broadcast layout request"); + String meetingID = Red5.getConnectionLocal().getScope().getName(); + String userId = getBbbSession().getInternalUserID(); + String pollId = (String) message.get("pollId"); + Integer questionId = (Integer) message.get("questionId"); + Integer answerId = (Integer) message.get("answerId"); + + // red5GW.broadcastLayout(meetingID, getBbbSession().getInternalUserID(), newlayout); + } + + public void showPollResult(Map message) { + log.debug("Received broadcast layout request"); + String meetingID = Red5.getConnectionLocal().getScope().getName(); + String newlayout = (String) message.get("layout"); + + if (newlayout == null || newlayout.isEmpty()) { + log.error("Invalid Broadcast Layout message. layout is null or empty."); + return; + } + + red5GW.broadcastLayout(meetingID, getBbbSession().getInternalUserID(), newlayout); + } + + public void startPoll(Map message) { + log.debug("Received broadcast layout request"); + String meetingID = Red5.getConnectionLocal().getScope().getName(); + String newlayout = (String) message.get("layout"); + + if (newlayout == null || newlayout.isEmpty()) { + log.error("Invalid Broadcast Layout message. layout is null or empty."); + return; + } + + red5GW.broadcastLayout(meetingID, getBbbSession().getInternalUserID(), newlayout); + } + + public void stopPoll(Map message) { + log.debug("Received lock layout request"); + String meetingID = Red5.getConnectionLocal().getScope().getName(); + String newlayout = (String) message.get("layout"); + Boolean lock = (Boolean) message.get("lock"); + Boolean viewersOnly = (Boolean) message.get("viewersOnly"); + String layout; + + if (newlayout == null || newlayout.isEmpty()) { + layout = null; + } else { + layout = newlayout; + } + + if (lock == null) { + log.error("Invalid Lock Layout message. lock in null."); + return; + } + + if (viewersOnly == null) { + log.error("Invalid Lock Layout message. viewersOnly is null"); + return; + } + + red5GW.lockLayout(meetingID, getBbbSession().getInternalUserID(), lock, viewersOnly, layout); + } + + private BigBlueButtonSession getBbbSession() { + return (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION); + } +} diff --git a/bigbluebutton-apps/src/main/resources/application.conf b/bigbluebutton-apps/src/main/resources/application.conf deleted file mode 100755 index ccb73a2910..0000000000 --- a/bigbluebutton-apps/src/main/resources/application.conf +++ /dev/null @@ -1,23 +0,0 @@ -akka { - actor { - debug { - receive = on - } - } - loglevel = DEBUG - stdout-loglevel = "DEBUG" -} - -spray.can.server { - request-timeout = 10s -} - -service { - host = "192.168.153.144" - port = 8989 -} - -api { - channel = "bbb-apps-channel" - source = "bigbluebutton-apps" -} \ No newline at end of file diff --git a/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml b/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml index eff7a4ce80..f9709f7d95 100755 --- a/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml +++ b/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml @@ -87,6 +87,10 @@ with BigBlueButton; if not, see . + + + +