diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java index 03e098f5eec15887b6a59e23f519ae34201cde33..0a158a53f6609e4426df6ce49e8068608885e03d 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java @@ -680,7 +680,7 @@ public class MeetingService implements MessageListener { } catch (URISyntaxException e) { log.error("URI Syntax error in callback url=[{}]", callbackUrl, e); } - callbackUrlService.handleMessage(new MeetingEndedEvent(callbackUrl)); + callbackUrlService.handleMessage(new MeetingEndedEvent(m.getInternalId(), m.getExternalId(), m.getName(), callbackUrl)); } processRemoveEndedMeeting(message); diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java index 0153043b3f61ed1febc8c7ae8dfdfd9a6dc9ff56..d55079f549b50a91c7953c07031129f0a8eae192 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/CallbackUrlService.java @@ -4,11 +4,9 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -17,10 +15,12 @@ import org.apache.http.impl.nio.client.HttpAsyncClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; + public class CallbackUrlService { private static Logger log = LoggerFactory.getLogger(CallbackUrlService.class); - private BlockingQueue<ICallbackEvent> receivedMessages = new LinkedBlockingQueue<ICallbackEvent>(); + private BlockingQueue<DelayCallback> receivedMessages = new DelayQueue<DelayCallback>(); private volatile boolean processMessage = false; private static final int MAX_REDIRECTS = 5; @@ -43,7 +43,7 @@ public class CallbackUrlService { public void run() { while (processMessage) { try { - ICallbackEvent msg = receivedMessages.take(); + DelayCallback msg = receivedMessages.take(); processMessage(msg); } catch (InterruptedException e) { log.warn("Error while taking received message from queue."); @@ -58,19 +58,96 @@ public class CallbackUrlService { } - private void processMessage(final ICallbackEvent msg) { + private void processMessage(final DelayCallback msg) { Runnable task = new Runnable() { public void run() { - - fetchCallbackUrl(msg.getCallbackUrl()); + MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent; + if (fetchCallbackUrl(msg.callbackEvent.getCallbackUrl())) { + Map<String, Object> logData = new HashMap<>(); + logData.put("meetingId", event.meetingid); + logData.put("externalMeetingId", event.extMeetingid); + logData.put("name",event.name); + logData.put("callback", event.getCallbackUrl()); + logData.put("attempts", msg.numAttempts); + logData.put("logCode", "callback_success"); + logData.put("description", "Callback successful."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info(" --analytics-- data={}", logStr); + } else { + schedRetryCallback(msg); + } } }; runExec.execute(task); } + private void schedCallback(final DelayCallback msg, long delayInMillis, int numAttempt) { + MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent; + Map<String, Object> logData = new HashMap<>(); + logData.put("meetingId", event.meetingid); + logData.put("externalMeetingId", event.extMeetingid); + logData.put("name",event.name); + logData.put("callback", event.getCallbackUrl()); + logData.put("attempts", msg.numAttempts); + logData.put("retryInMs", delayInMillis); + logData.put("logCode", "callback_failed_retry"); + logData.put("description", "Callback failed but retrying."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info(" --analytics-- data={}", logStr); + + DelayCallback dc = new DelayCallback(event, delayInMillis, numAttempt); + receivedMessages.add(dc); + } + + private void giveupCallback(final DelayCallback msg) { + MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent; + Map<String, Object> logData = new HashMap<>(); + logData.put("meetingId", event.meetingid); + logData.put("externalMeetingId", event.extMeetingid); + logData.put("name",event.name); + logData.put("callback", event.getCallbackUrl()); + logData.put("attempts", msg.numAttempts); + logData.put("logCode", "callback_failed_give_up"); + logData.put("description", "Callback failed and giving up."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info(" --analytics-- data={}", logStr); + } + private void schedRetryCallback(final DelayCallback msg) { + MeetingEndedEvent event = (MeetingEndedEvent) msg.callbackEvent; + + switch (msg.numAttempts) { + case 1: + schedCallback(msg, 30_000 /** 30sec **/, 2); + break; + case 2: + schedCallback(msg, 60_000 /** 1min **/, 3); + break; + case 3: + schedCallback(msg, 120_000 /** 2min **/, 4); + break; + case 4: + schedCallback(msg, 300_000 /** 5min **/, 5); + break; + default: + giveupCallback(msg); + } + } + public void handleMessage(ICallbackEvent message) { - receivedMessages.add(message); + long delayInMillis = -1000 /**Send right away **/; + int numAttempt = 1; + DelayCallback dc = new DelayCallback(message, delayInMillis, numAttempt); + receivedMessages.add(dc); } private String followRedirect(String redirectUrl, int redirectCount, String origUrl) { @@ -133,7 +210,8 @@ public class CallbackUrlService { Future<HttpResponse> future = httpclient.execute(request, null); HttpResponse response = future.get(); - success = response.getStatusLine().getStatusCode() == 200; + // Consider 2xx response code as success. + success = (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300); } catch (java.lang.InterruptedException ex) { log.error("Interrupted exception while calling url {}", callbackUrl); } catch (java.util.concurrent.ExecutionException ex) { diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java new file mode 100755 index 0000000000000000000000000000000000000000..f72aab3b847a9c3ad09469e9f19ffb6593ec6449 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/DelayCallback.java @@ -0,0 +1,27 @@ +package org.bigbluebutton.web.services.callback; + +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +public class DelayCallback implements Delayed{ + public final ICallbackEvent callbackEvent; + public final long callTime; + public final int numAttempts; + + public DelayCallback(ICallbackEvent event, long delayInMillis, int numAttempts) { + this.callbackEvent = event; + this.callTime = System.currentTimeMillis() + delayInMillis; + this.numAttempts = numAttempts; + } + + @Override + public long getDelay(TimeUnit unit) { + long diff = callTime - System.currentTimeMillis(); + return unit.convert(diff, TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(Delayed o) { + return new Long(this.callTime - ((DelayCallback) o).callTime).intValue(); + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java index 3eb9b13784f44402cb182fce821e18df37c6ac2c..807b03a118030a94ae7207a5059a6457881515d4 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/web/services/callback/MeetingEndedEvent.java @@ -2,9 +2,15 @@ package org.bigbluebutton.web.services.callback; public class MeetingEndedEvent implements ICallbackEvent { private final String callbackUrl; + public final String meetingid; + public final String extMeetingid; + public final String name; - public MeetingEndedEvent(String callbackUrl) { + public MeetingEndedEvent(String mid, String extMid, String name, String callbackUrl) { this.callbackUrl = callbackUrl; + this.meetingid = mid; + this.extMeetingid = extMid; + this.name = name; } public String getCallbackUrl() {