package com.psddev.cms.rtc; import com.psddev.dari.db.Query; import com.psddev.dari.util.ClassFinder; import com.psddev.dari.util.TypeDefinition; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.BiConsumer; /** * For capturing any updates to the instances of the given {@code <T>} type * and sending some data back to the client. * * <p>The broadcast can be received in JavaScript using:</p> * * <p><blockquote><pre> * define([ 'v3/rtc' ], function(rtc) { * rtc.receive('full.RtcBroadcastClassName', function(data) { * // Do something with data. * }); * }); * </pre></blockquote></p> * * @param <T> * The type of instances that the implementation should handle. * * @since 3.1 */ public interface RtcBroadcast<T> { static <T> void forEachBroadcast(T object, BiConsumer<RtcBroadcast<T>, Map<String, Object>> consumer) { if (object instanceof RtcEvent) { RtcEvent event = (RtcEvent) object; UUID sessionId = event.as(RtcEvent.Data.class).getSessionId(); if (sessionId != null && !Query.from(RtcSession.class) .where("_id = ?", sessionId) .hasMoreThan(0L)) { event.getState().delete(); return; } } Set<RtcBroadcast<T>> broadcasts = new HashSet<>(); BROADCAST: for (Class<?> c : ClassFinder.Static.findClasses(RtcBroadcast.class)) { if (c.isInterface() || Modifier.isAbstract(c.getModifiers())) { continue; } for (Type broadcastInterface : c.getGenericInterfaces()) { if (broadcastInterface instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) broadcastInterface; Type rt = pt.getRawType(); if (rt instanceof Class && RtcBroadcast.class.isAssignableFrom((Class<?>) rt)) { Type[] args = pt.getActualTypeArguments(); if (args.length > 0) { Type arg = args[0]; if (arg instanceof Class && !((Class<?>) arg).isInstance(object)) { continue BROADCAST; } else { break; } } } } } @SuppressWarnings("unchecked") Class<RtcBroadcast<T>> broadcastClass = (Class<RtcBroadcast<T>>) c; RtcBroadcast<T> broadcast = TypeDefinition.getInstance(broadcastClass).newInstance(); Map<String, Object> data = broadcast.create(object); if (data != null) { consumer.accept(broadcast, data); } } } /** * Returns {@code true} if the given {@code data} should be broadcast * to the user with the given {@code currentUserId}. * * @param data * Can't be {@code null}. * * @param currentUserId * Can't be {@code null}. */ boolean shouldBroadcast(Map<String, Object> data, UUID currentUserId); /** * Creates the broadcast data based on the given {@code object}. * * @param object * Can't be {@code null}. * * @return If {@code null}, doesn't send anything back to the client. */ Map<String, Object> create(T object); }