// Path: src/main/java/org/epics/ca/util/logging/LibraryLogManager.java
// public class LibraryLogManager
// {
// 
// /*- Public attributes --------------------------------------------------------*/
// /*- Private attributes -------------------------------------------------------*/
// 
//    private static final StreamHandler flushingStandardOutputStreamHandler;
// 
//    static
//    {
//       // Could consider here setting the default locale for this instance of
//       // the Java Virtual Machine. But currently (2020-05-10) it is not
//       // considered that the library "owns" this decision.
//       // Locale.setDefault( Locale.ROOT );
//       // Note: the definition below determines the format of all log messages
//       // emitted by the CA library.
//       System.setProperty( "java.util.logging.SimpleFormatter.format", "%1$tF %1$tT.%1$tL %3$s %4$s %5$s %6$s %n" );
// 
//       // Create a stream handler that is like the normal output stream handler except
//       // it will ensure that each message gets flushed to the console. Note: this may
//       // impose some performance penalty during testing if copious messages are
//       // being logged. During normal CA usage the library does not emit many
//       // messages so in the normal situation this limitation should not be important.
//       flushingStandardOutputStreamHandler = new StreamHandler( System.out, new SimpleFormatter() )
//       {
//          @Override
//          public synchronized void publish( final LogRecord record )
//          {
//             super.publish( record );
//             flush();
//          }
//       };
// 
//       // The output handler will emit all messages that are sent to it. It is up
//       // to the individual loggers to determine what is appropriate for their
//       // particular operating contexts.
//       flushingStandardOutputStreamHandler.setLevel( Level.ALL );
//    }
// 
// 
// /*- Main ---------------------------------------------------------------------*/
// /*- Constructor --------------------------------------------------------------*/
// /*- Public methods -----------------------------------------------------------*/
// 
//    /**
//     * Returns a logger for the specified class that will send messages to the
//     * standard output stream providing their log level exceeds the level defined 
//     * by the CA_LIBRARY_LOG_LEVEL system property.
//     *
//     * When CA_LIBRARY_LOG_LEVEL is not defined all messages of Level.INFO and
//     * above will be logged.
//     *
//     * @param clazz the class that the log messages will be associated with.
//     * @return the configured logger.
//     * 
//     * throws NullPointerException if clazz was null.
//     * throws IllegalArgumentException if the string token associated with
//     * CA_LIBRARY_LOG_LEVEL could not be interpreted as a valid log level.
//     */
//    public static Logger getLogger( Class<?> clazz )
//    {
//       Validate.notNull( clazz );
//       return getLogger( clazz, LibraryConfiguration.getInstance().getLibraryLogLevel() );
//    }
// 
//    /**
//     * Returns a logger for the specified class that will send messages to the
//     * standard output stream providing their log level exceeds the specified
//     * log level.
//     *
//     * @param clazz the class that the logger will be associated with
//     *     when logging messages.
//     *
//     * @param logLevel Set the log level specifying which message levels
//     *    will be sent to the standard output stream by this logger. Message levels
//     *    lower than this value will be discarded. The value Level.OFF can be
//     *    used to completely turn off logging.
//     *
//     * @return the configured logger.
//     */
//    public static Logger getLogger( Class<?> clazz, Level logLevel )
//    {
//       // Currently (2020-05-14) the simple name is used in the log. This
//       // is considerably shorter than the fully qualified class name. Other
//       // implementations are possible. For example the Spring framework
//       // takes the approach of shortening the package names in the FQN to
//       // a single character. Another approach would be to extract the name
//       // automatically from the calling stack.
//       final Logger logger = Logger.getLogger( clazz.getSimpleName() );
//       logger.setUseParentHandlers( false );
// 
//       if ( logger.getHandlers().length == 0 )
//       {
//          logger.addHandler( flushingStandardOutputStreamHandler );
//       }
//       else
//       {
//          System.out.println( "\nWARNING: More than one logger defined for class: '" + clazz.getSimpleName() + "'.\n" );
//       }
//       logger.setLevel( logLevel );
// 
//       return logger;
//    }
// 
// 
// /*- Package-level methods ----------------------------------------------------*/
// 
//    /**
//     * Provided to enable tests only.
//     * @param logger the logger whose handlers are to be disposed.
//     */
//    static void disposeLogger( Logger logger)
//    {
//       if ( logger.getHandlers().length == 1 )
//       {
//          logger.removeHandler( flushingStandardOutputStreamHandler );
//       }
//    }
// 
// /*- Private methods ----------------------------------------------------------*/
// /*- Nested Classes -----------------------------------------------------------*/
// 
// }


import org.apache.commons.lang3.Validate;
import org.epics.ca.util.logging.LibraryLogManager;
import java.net.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
      logger.finest( "Attempting to send 'CA_RSRV_IS_UP' (Beacon) message to " + clientList.size() + " CA Repeater clients.");

      final List<CARepeaterClientProxy> failedNotificationList = clientList.stream()
            .filter( client -> ! client.getClientListeningSocketAddress().equals( excluded ) )
            .filter( client -> ! client.sendCaServerBeaconMessage( serverCaVersionNumber, serverTcpListeningPort, serverBeaconId, serverAddress ) )
            .collect( Collectors.toList() );

      logger.finest( "There were " + failedNotificationList.size() + " send failures." );
      return failedNotificationList;
   }

   /**
    * Sequentially sends the supplied datagram to all registered CA Repeater Clients
    * with the exception of any that might be listening on the excluded socket address.
    *
    * The datagram packet must not explicitly set the destination internet address
    * since this is determined by the CA Repeater client listening address which was
    * set during class construction.
    *
    * @param packet the datagram packet to send.
    * @param excluded the socket address of any client who should be excluded from
    * notification. This parameter can be used to prevent CA repeater clients who
    * originate UDP messages from having those messages reflected back to themselves.
    *
    * @return list of CA Repeater Client Proxies (if any) who failed to transmit the message.
    * @throws NullPointerException if the packet argument was null.
    * @throws NullPointerException if the excluded argument was null.
    * @throws IllegalArgumentException if the packet's port was specified.
    * @throws IllegalArgumentException if the packet's internet address was specified.
    */
// Path: src/main/java/org/epics/ca/util/logging/LibraryLogManager.java
// public class LibraryLogManager
// {
// 
// /*- Public attributes --------------------------------------------------------*/
// /*- Private attributes -------------------------------------------------------*/
// 
//    private static final StreamHandler flushingStandardOutputStreamHandler;
// 
//    static
//    {
//       // Could consider here setting the default locale for this instance of
//       // the Java Virtual Machine. But currently (2020-05-10) it is not
//       // considered that the library "owns" this decision.
//       // Locale.setDefault( Locale.ROOT );
//       // Note: the definition below determines the format of all log messages
//       // emitted by the CA library.
//       System.setProperty( "java.util.logging.SimpleFormatter.format", "%1$tF %1$tT.%1$tL %3$s %4$s %5$s %6$s %n" );
// 
//       // Create a stream handler that is like the normal output stream handler except
//       // it will ensure that each message gets flushed to the console. Note: this may
//       // impose some performance penalty during testing if copious messages are
//       // being logged. During normal CA usage the library does not emit many
//       // messages so in the normal situation this limitation should not be important.
//       flushingStandardOutputStreamHandler = new StreamHandler( System.out, new SimpleFormatter() )
//       {
//          @Override
//          public synchronized void publish( final LogRecord record )
//          {
//             super.publish( record );
//             flush();
//          }
//       };
// 
//       // The output handler will emit all messages that are sent to it. It is up
//       // to the individual loggers to determine what is appropriate for their
//       // particular operating contexts.
//       flushingStandardOutputStreamHandler.setLevel( Level.ALL );
//    }
// 
// 
// /*- Main ---------------------------------------------------------------------*/
// /*- Constructor --------------------------------------------------------------*/
// /*- Public methods -----------------------------------------------------------*/
// 
//    /**
//     * Returns a logger for the specified class that will send messages to the
//     * standard output stream providing their log level exceeds the level defined 
//     * by the CA_LIBRARY_LOG_LEVEL system property.
//     *
//     * When CA_LIBRARY_LOG_LEVEL is not defined all messages of Level.INFO and
//     * above will be logged.
//     *
//     * @param clazz the class that the log messages will be associated with.
//     * @return the configured logger.
//     * 
//     * throws NullPointerException if clazz was null.
//     * throws IllegalArgumentException if the string token associated with
//     * CA_LIBRARY_LOG_LEVEL could not be interpreted as a valid log level.
//     */
//    public static Logger getLogger( Class<?> clazz )
//    {
//       Validate.notNull( clazz );
//       return getLogger( clazz, LibraryConfiguration.getInstance().getLibraryLogLevel() );
//    }
// 
//    /**
//     * Returns a logger for the specified class that will send messages to the
//     * standard output stream providing their log level exceeds the specified
//     * log level.
//     *
//     * @param clazz the class that the logger will be associated with
//     *     when logging messages.
//     *
//     * @param logLevel Set the log level specifying which message levels
//     *    will be sent to the standard output stream by this logger. Message levels
//     *    lower than this value will be discarded. The value Level.OFF can be
//     *    used to completely turn off logging.
//     *
//     * @return the configured logger.
//     */
//    public static Logger getLogger( Class<?> clazz, Level logLevel )
//    {
//       // Currently (2020-05-14) the simple name is used in the log. This
//       // is considerably shorter than the fully qualified class name. Other
//       // implementations are possible. For example the Spring framework
//       // takes the approach of shortening the package names in the FQN to
//       // a single character. Another approach would be to extract the name
//       // automatically from the calling stack.
//       final Logger logger = Logger.getLogger( clazz.getSimpleName() );
//       logger.setUseParentHandlers( false );
// 
//       if ( logger.getHandlers().length == 0 )
//       {
//          logger.addHandler( flushingStandardOutputStreamHandler );
//       }
//       else
//       {
//          System.out.println( "\nWARNING: More than one logger defined for class: '" + clazz.getSimpleName() + "'.\n" );
//       }
//       logger.setLevel( logLevel );
// 
//       return logger;
//    }
// 
// 
// /*- Package-level methods ----------------------------------------------------*/
// 
//    /**
//     * Provided to enable tests only.
//     * @param logger the logger whose handlers are to be disposed.
//     */
//    static void disposeLogger( Logger logger)
//    {
//       if ( logger.getHandlers().length == 1 )
//       {
//          logger.removeHandler( flushingStandardOutputStreamHandler );
//       }
//    }
// 
// /*- Private methods ----------------------------------------------------------*/
// /*- Nested Classes -----------------------------------------------------------*/
// 
// }


// Path: src/main/java/org/epics/ca/impl/repeater/CARepeaterClientManager.java
import org.apache.commons.lang3.Validate;
import org.epics.ca.util.logging.LibraryLogManager;
import java.net.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

      logger.finest( "Attempting to send 'CA_RSRV_IS_UP' (Beacon) message to " + clientList.size() + " CA Repeater clients.");

      final List<CARepeaterClientProxy> failedNotificationList = clientList.stream()
            .filter( client -> ! client.getClientListeningSocketAddress().equals( excluded ) )
            .filter( client -> ! client.sendCaServerBeaconMessage( serverCaVersionNumber, serverTcpListeningPort, serverBeaconId, serverAddress ) )
            .collect( Collectors.toList() );

      logger.finest( "There were " + failedNotificationList.size() + " send failures." );
      return failedNotificationList;
   }

   /**
    * Sequentially sends the supplied datagram to all registered CA Repeater Clients
    * with the exception of any that might be listening on the excluded socket address.
    *
    * The datagram packet must not explicitly set the destination internet address
    * since this is determined by the CA Repeater client listening address which was
    * set during class construction.
    *
    * @param packet the datagram packet to send.
    * @param excluded the socket address of any client who should be excluded from
    * notification. This parameter can be used to prevent CA repeater clients who
    * originate UDP messages from having those messages reflected back to themselves.
    *
    * @return list of CA Repeater Client Proxies (if any) who failed to transmit the message.
    * @throws NullPointerException if the packet argument was null.
    * @throws NullPointerException if the excluded argument was null.
    * @throws IllegalArgumentException if the packet's port was specified.
    * @throws IllegalArgumentException if the packet's internet address was specified.
    */
   private List<CARepeaterClientProxy> sendDatagramToRegisteredClients( DatagramPacket packet, InetSocketAddress excluded )