99import java .net .*;
1010import java .util .*;
1111import java .util .concurrent .ConcurrentHashMap ;
12+ import java .util .concurrent .atomic .AtomicBoolean ;
1213import java .util .concurrent .atomic .AtomicLong ;
1314import java .util .concurrent .atomic .AtomicReference ;
1415
@@ -53,6 +54,12 @@ public class DeviceFinder extends LifecycleParticipant {
5354 */
5455 private static final AtomicLong firstDeviceTime = new AtomicLong (0 );
5556
57+ /**
58+ * Indicates whether we have seen any players older than a CDJ-3000, which are unable to respond to
59+ * metadata queries from more than three other players.
60+ */
61+ private static final AtomicBoolean limit3PlayersSeen = new AtomicBoolean (false );
62+
5663 /**
5764 * Check whether we are presently listening for device announcements.
5865 *
@@ -180,6 +187,11 @@ public boolean isAddressIgnored(InetAddress address) {
180187 return ignoredAddresses .contains (address );
181188 }
182189
190+ /**
191+ * The names of devices that are able to respond to metadata requests from more than three other devices.
192+ */
193+ public final Set <String > METADATA_FLEXIBLE_DEVICES = Set .of ("CDJ-3000" , "XDJ-AZ" );
194+
183195 /**
184196 * Handle a device announcement packet we have received.
185197 *
@@ -347,6 +359,17 @@ public Set<DeviceAnnouncement> getCurrentDevices() {
347359 return Set .copyOf (devices .values ());
348360 }
349361
362+ /**
363+ * Check whether we have seen any players that are old enough to only be able to answer metadata queries
364+ * from at most three other players.
365+ *
366+ * @return {code true} if any device on the network would return {code true} when passed to {@link #isDeviceMetadataLimited(DeviceAnnouncement)}
367+ */
368+ @ API (status = API .Status .EXPERIMENTAL )
369+ public boolean isAnyDeviceLimitedToThreeDatabaseClients () {
370+ return limit3PlayersSeen .get ();
371+ }
372+
350373 /**
351374 * Find and return the device announcement that was most recently received from a device identifying itself
352375 * with the specified device number, if any.
@@ -381,7 +404,7 @@ public DeviceAnnouncement getLatestAnnouncementFrom(int deviceNumber) {
381404 * <p>Device announcements are delivered to listeners on the
382405 * <a href="https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html">Event Dispatch thread</a>,
383406 * so it is fine to interact with user interface objects in listener methods. Any code in the listener method
384- * must finish quickly, or unhandled events will back up and the user interface will be come unresponsive.</p>
407+ * must finish quickly, or unhandled events will back up and the user interface will become unresponsive.</p>
385408 *
386409 * @param listener the device announcement listener to add
387410 */
@@ -416,12 +439,30 @@ public Set<DeviceAnnouncementListener> getDeviceAnnouncementListeners() {
416439 return Set .copyOf (deviceListeners );
417440 }
418441
442+ /**
443+ * Checks whether the device is old enough that it is unable to respond to metadata queries from more than
444+ * three other players.
445+ *
446+ * @param announcement the device to be examined
447+ *
448+ * @return {code true} if the device appears to be a CDJ that is not known to support metadata queries from more than three other devices
449+ */
450+ @ API (status = API .Status .EXPERIMENTAL )
451+ public boolean isDeviceMetadataLimited (DeviceAnnouncement announcement ) {
452+ return announcement .getDeviceNumber () < 7 && !METADATA_FLEXIBLE_DEVICES .contains (announcement .getDeviceName ());
453+ }
454+
419455 /**
420456 * Send a device found announcement to all registered listeners.
421457 *
422458 * @param announcement the message announcing the new device
423459 */
424460 private void deliverFoundAnnouncement (final DeviceAnnouncement announcement ) {
461+ // If this is a metadata-query-limited device, note that there is now at least one on the network.
462+ if (isDeviceMetadataLimited (announcement )) {
463+ limit3PlayersSeen .set (true );
464+ }
465+ // Send out the device announcement.
425466 for (final DeviceAnnouncementListener listener : getDeviceAnnouncementListeners ()) {
426467 SwingUtilities .invokeLater (() -> {
427468 try {
@@ -438,7 +479,16 @@ private void deliverFoundAnnouncement(final DeviceAnnouncement announcement) {
438479 *
439480 * @param announcement the last message received from the vanished device
440481 */
482+ @ API (status = API .Status .EXPERIMENTAL )
441483 private void deliverLostAnnouncement (final DeviceAnnouncement announcement ) {
484+ // If the device we lost was metadata-limited, see if it was the last one, and if so, update our tracker.
485+ if (isDeviceMetadataLimited (announcement )) {
486+ if (getCurrentDevices ().stream ().noneMatch (this ::isDeviceMetadataLimited )) {
487+ limit3PlayersSeen .set (false );
488+ }
489+ }
490+
491+ // Send out the announcement.
442492 for (final DeviceAnnouncementListener listener : getDeviceAnnouncementListeners ()) {
443493 SwingUtilities .invokeLater (() -> {
444494 try {
0 commit comments