Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
id "org.owasp.dependencycheck" version "12.1.3"
}

ext.projectVersion = '3.2.5'
ext.projectVersion = '3.2.6'
ext.isReleaseVersion = !ext.projectVersion.endsWith('SNAPSHOT')

ext.mavenRepoUrl = project.properties['mavenRepoUrl'] ?: 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public abstract class FormReplyActivity<C extends FormReplyContext>
/** {@inheritDoc} */
@Override
protected void bindToRealTimeEventsSource(Consumer<RealTimeEventListener> realTimeEventsSource) {
bindOnSymphonyElementsAction(realTimeEventsSource, this::processEvent);
bindOnSymphonyElementsAction(realTimeEventsSource, this::processEvent, this);
}

/** {@inheritDoc} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class UserJoinedRoomActivity<C extends UserJoinedRoomContext>
*/
@Override
protected void bindToRealTimeEventsSource(Consumer<RealTimeEventListener> realTimeEventsSource) {
bindOnUserJoinedRoom(realTimeEventsSource, this::processEvent);
bindOnUserJoinedRoom(realTimeEventsSource, this::processEvent, this);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package com.symphony.bdk.core.service.datafeed.util;

import com.symphony.bdk.core.activity.AbstractActivity;
import com.symphony.bdk.core.service.datafeed.RealTimeEventListener;
import com.symphony.bdk.gen.api.model.V4Initiator;
import com.symphony.bdk.gen.api.model.V4MessageSent;
import com.symphony.bdk.gen.api.model.V4SymphonyElementsAction;

import com.symphony.bdk.gen.api.model.V4UserJoinedRoom;

import org.apiguardian.api.API;

import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import javax.annotation.Nullable;

/**
* Utility class used to attach a method call (defined by a {@link BiConsumer}) to a specific real-time event.
*/
Expand All @@ -26,47 +29,40 @@ public RealTimeEventsBinder() {
* Bind "onMessageSent" real-time event to a target method.
*
* @param subscriber The Datafeed real-time events subscriber.
* @param target Target method.
* @param target Target method.
* @param activity The activity that creates the listener. It can be null, but in that case it will not be possible
* to unsubscribe the activity later.
*/
public static void bindOnMessageSent(Consumer<RealTimeEventListener> subscriber, BiConsumer<V4Initiator, V4MessageSent> target) {
subscriber.accept(new RealTimeEventListener() {

@Override
public void onMessageSent(V4Initiator initiator, V4MessageSent event) {
target.accept(initiator, event);
}
});
public static void bindOnMessageSent(Consumer<RealTimeEventListener> subscriber,
BiConsumer<V4Initiator, V4MessageSent> target, @Nullable AbstractActivity<V4MessageSent, ?> activity) {
subscriber.accept(new OnMessageSent(activity, target));
}

/**
* Bind "onSymphonyElementsAction" real-time event to a target method.
*
* @param subscriber The Datafeed real-time events subscriber.
* @param target Target method.
* @param target Target method.
* @param activity The activity that creates the listener. It can be null, but in that case it will not be possible
* to unsubscribe the activity later.
*/
public static void bindOnSymphonyElementsAction(Consumer<RealTimeEventListener> subscriber, BiConsumer<V4Initiator, V4SymphonyElementsAction> target) {
subscriber.accept(new RealTimeEventListener() {

@Override
public void onSymphonyElementsAction(V4Initiator initiator, V4SymphonyElementsAction event) {
target.accept(initiator, event);
}
});
public static void bindOnSymphonyElementsAction(Consumer<RealTimeEventListener> subscriber,
BiConsumer<V4Initiator, V4SymphonyElementsAction> target,
@Nullable AbstractActivity<V4SymphonyElementsAction, ?> activity) {
subscriber.accept(new OnSymphonyElementsAction(activity, target));
}

/**
* Bind "onUserJoinedRoom" real-time event to a target method.
*
* @param subscriber The Datafeed real-time events subscriber.
* @param target Target method.
* @param target Target method.
* @param activity The activity that creates the listener. It can be null, but in that case it will not be possible
* to unsubscribe the activity later.
*/
public static void bindOnUserJoinedRoom(Consumer<RealTimeEventListener> subscriber, BiConsumer<V4Initiator, V4UserJoinedRoom> target) {
subscriber.accept(new RealTimeEventListener() {
@Override
public void onUserJoinedRoom(V4Initiator initiator, V4UserJoinedRoom event) {
target.accept(initiator, event);
}
});
public static void bindOnUserJoinedRoom(Consumer<RealTimeEventListener> subscriber,
BiConsumer<V4Initiator, V4UserJoinedRoom> target, @Nullable AbstractActivity<V4UserJoinedRoom, ?> activity) {
subscriber.accept(new OnUserJoinedRoom(activity, target));
}

/**
Expand All @@ -78,4 +74,81 @@ public void onUserJoinedRoom(V4Initiator initiator, V4UserJoinedRoom event) {
public static void bindRealTimeListener(Consumer<RealTimeEventListener> consumer, RealTimeEventListener listener) {
consumer.accept(listener);
}

/**
* Internal private records used to create listeners from an {@link AbstractActivity}.
* They keep a reference to the source {@link AbstractActivity}, which is used only for equality validation.
* This ensures that multiple identical listeners from the same {@link AbstractActivity} are not registered,
* and also allows the {@link AbstractActivity} to be unsubscribed later.
*/

private record OnMessageSent(AbstractActivity<V4MessageSent, ?> activity,
BiConsumer<V4Initiator, V4MessageSent> target)
implements RealTimeEventListener {

@Override
public void onMessageSent(V4Initiator initiator, V4MessageSent event) {
target.accept(initiator, event);
}

@Override
public boolean equals(Object o) {
// If the listener is created with a null activity, there is no way to identify it later.
// For this reason, equals will always return false.
if (!(o instanceof OnMessageSent that) || this.activity == null) {return false;}
return activity.equals(that.activity);
}

@Override
public int hashCode() {
return Objects.hashCode(activity);
}
}


private record OnSymphonyElementsAction(AbstractActivity<V4SymphonyElementsAction, ?> activity,
BiConsumer<V4Initiator, V4SymphonyElementsAction> target)
implements RealTimeEventListener {

@Override
public void onSymphonyElementsAction(V4Initiator initiator, V4SymphonyElementsAction event) {
target.accept(initiator, event);
}

@Override
public boolean equals(Object o) {
// If the listener is created with a null activity, there is no way to identify it later.
// For this reason, equals will always return false.
if (!(o instanceof OnSymphonyElementsAction that) || this.activity == null) {return false;}
return activity.equals(that.activity);
}

@Override
public int hashCode() {
return Objects.hashCode(activity);
}
}


private record OnUserJoinedRoom(AbstractActivity<V4UserJoinedRoom, ?> activity,
BiConsumer<V4Initiator, V4UserJoinedRoom> target) implements RealTimeEventListener {

@Override
public void onUserJoinedRoom(V4Initiator initiator, V4UserJoinedRoom event) {
target.accept(initiator, event);
}

@Override
public boolean equals(Object o) {
// If the listener is created with a null activity, there is no way to identify it later.
// For this reason, equals will always return false.
if (!(o instanceof OnUserJoinedRoom that) || this.activity == null) {return false;}
return activity.equals(that.activity);
}

@Override
public int hashCode() {
return Objects.hashCode(activity);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import static org.junit.jupiter.api.Assertions.*;

import com.symphony.bdk.core.activity.AbstractActivity;
import com.symphony.bdk.core.activity.ActivityContext;
import com.symphony.bdk.core.activity.ActivityMatcher;
import com.symphony.bdk.core.activity.model.ActivityInfo;
import com.symphony.bdk.core.service.datafeed.EventException;
import com.symphony.bdk.core.service.datafeed.RealTimeEventListener;
import com.symphony.bdk.gen.api.model.V4Initiator;
import com.symphony.bdk.gen.api.model.V4MessageSent;
Expand Down Expand Up @@ -38,29 +43,144 @@ void testConstructorJustToMakeJacocoHappy() {
void testBindOnMessageSent() {
final AtomicBoolean methodCalled = new AtomicBoolean(false);
final BiConsumer<V4Initiator, V4MessageSent> methodToBind = (initiator, v4MessageSent) -> methodCalled.set(true);
RealTimeEventsBinder.bindOnMessageSent(this.realTimeEventsProvider::setListener, methodToBind);
RealTimeEventsBinder.bindOnMessageSent(this.realTimeEventsProvider::setListener, methodToBind, null);
this.realTimeEventsProvider.trigger(l -> l.onMessageSent(new V4Initiator(), new V4MessageSent()));
assertTrue(methodCalled.get());
}

@Test
void testBindOnMessageSentEqualsOnActivity() {
final BiConsumer<V4Initiator, V4MessageSent> methodToBind1 = (initiator, v4MessageSent) -> {};
final BiConsumer<V4Initiator, V4MessageSent> methodToBind2 = (initiator, v4MessageSent) -> {};
AbstractActivity<V4MessageSent, ?> activity = new AbstractActivity<>() {

@Override
protected ActivityMatcher matcher() throws EventException {
return null;
}

@Override
protected ActivityInfo info() {
return null;
}

@Override
protected void bindToRealTimeEventsSource(Consumer realTimeEventsSource) {

}

@Override
protected void onActivity(ActivityContext context) throws EventException {

}
};


RealTimeEventsBinder.bindOnMessageSent(this.realTimeEventsProvider::setListener, methodToBind1, activity);
RealTimeEventListener listener1 = this.realTimeEventsProvider.listener;

RealTimeEventsBinder.bindOnMessageSent(this.realTimeEventsProvider::setListener, methodToBind2, activity);
RealTimeEventListener listener2 = this.realTimeEventsProvider.listener;

assertTrue(listener1 != listener2);
assertEquals(listener1, listener2);
assertEquals(listener1.hashCode(), listener2.hashCode());
}

@Test
void testBindOnSymphonyElementsAction() {
final AtomicBoolean methodCalled = new AtomicBoolean(false);
final BiConsumer<V4Initiator, V4SymphonyElementsAction> methodToBind = (initiator, v4SymphonyElementsAction) -> methodCalled.set(true);
RealTimeEventsBinder.bindOnSymphonyElementsAction(this.realTimeEventsProvider::setListener, methodToBind);
RealTimeEventsBinder.bindOnSymphonyElementsAction(this.realTimeEventsProvider::setListener, methodToBind, null);
this.realTimeEventsProvider.trigger(l -> l.onSymphonyElementsAction(new V4Initiator(), new V4SymphonyElementsAction()));
assertTrue(methodCalled.get());
}

@Test
void testBindOnSymphonyElementsActionEqualsOnActivity() {
final BiConsumer<V4Initiator, V4SymphonyElementsAction> methodToBind1 = (initiator, v4SymphonyElementsAction) -> {};
final BiConsumer<V4Initiator, V4SymphonyElementsAction> methodToBind2 = (initiator, v4SymphonyElementsAction) -> {};
AbstractActivity<V4SymphonyElementsAction, ?> activity = new AbstractActivity<>() {

@Override
protected ActivityMatcher matcher() throws EventException {
return null;
}

@Override
protected ActivityInfo info() {
return null;
}

@Override
protected void bindToRealTimeEventsSource(Consumer realTimeEventsSource) {

}

@Override
protected void onActivity(ActivityContext context) throws EventException {

}
};

RealTimeEventsBinder.bindOnSymphonyElementsAction(this.realTimeEventsProvider::setListener, methodToBind1, activity);
RealTimeEventListener listener1 = this.realTimeEventsProvider.listener;

RealTimeEventsBinder.bindOnSymphonyElementsAction(this.realTimeEventsProvider::setListener, methodToBind2, activity);
RealTimeEventListener listener2 = this.realTimeEventsProvider.listener;

assertTrue(listener1 != listener2);
assertEquals(listener1, listener2);
assertEquals(listener1.hashCode(), listener2.hashCode());
}

@Test
void testBindOnUserJoinedRoom() {
final AtomicBoolean methodCalled = new AtomicBoolean(false);
final BiConsumer<V4Initiator, V4UserJoinedRoom> methodToBind = ((initiator, v4UserJoinedRoom) -> methodCalled.set(true));
RealTimeEventsBinder.bindOnUserJoinedRoom(this.realTimeEventsProvider::setListener, methodToBind);
RealTimeEventsBinder.bindOnUserJoinedRoom(this.realTimeEventsProvider::setListener, methodToBind, null);
this.realTimeEventsProvider.trigger(l -> l.onUserJoinedRoom(new V4Initiator(), new V4UserJoinedRoom()));
assertTrue(methodCalled.get());
}

@Test
void testBindOnUserJoinedRoomEqualsOnActivity() {
final BiConsumer<V4Initiator, V4UserJoinedRoom> methodToBind1 = (initiator, v4UserJoinedRoom) -> {};
final BiConsumer<V4Initiator, V4UserJoinedRoom> methodToBind2 = (initiator, v4UserJoinedRoom) -> {};
AbstractActivity<V4UserJoinedRoom, ?> activity = new AbstractActivity<>() {

@Override
protected ActivityMatcher matcher() throws EventException {
return null;
}

@Override
protected ActivityInfo info() {
return null;
}

@Override
protected void bindToRealTimeEventsSource(Consumer realTimeEventsSource) {

}

@Override
protected void onActivity(ActivityContext context) throws EventException {

}
};

RealTimeEventsBinder.bindOnUserJoinedRoom(this.realTimeEventsProvider::setListener, methodToBind1, activity);
RealTimeEventListener listener1 = this.realTimeEventsProvider.listener;

RealTimeEventsBinder.bindOnUserJoinedRoom(this.realTimeEventsProvider::setListener, methodToBind2, activity);
RealTimeEventListener listener2 = this.realTimeEventsProvider.listener;

assertTrue(listener1 != listener2);
assertEquals(listener1, listener2);
assertEquals(listener1.hashCode(), listener2.hashCode());
}

private static class RealTimeEventsProvider {

private RealTimeEventListener listener;
Expand Down
Loading