All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.discovery.service.NetBidibBonjourServiceRegistrationService Maven / Gradle / Ivy

package org.bidib.wizard.discovery.service;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.annotation.PreDestroy;
import javax.jmdns.JmDNS;
import javax.jmdns.NetworkTopologyDiscovery;
import javax.jmdns.ServiceInfo;
import javax.jmdns.impl.JmDNSImpl;

import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.wizard.api.model.event.ConsoleMessageEvent;
import org.bidib.wizard.api.service.console.ConsoleColor;
import org.bidib.wizard.common.event.WizardApplicationReadyEvent;
import org.bidib.wizard.common.model.settings.NetBidibSettingsInterface;
import org.bidib.wizard.common.utils.NetworkAddressHelper;
import org.bidib.wizard.core.model.settings.NetBidibSettings;
import org.bidib.wizard.core.utils.AopUtils;
import org.bidib.wizard.discovery.listener.BonjourServiceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;

public class NetBidibBonjourServiceRegistrationService implements ServiceRegistrationService, BonjourService {

    private static final Logger LOGGER = LoggerFactory.getLogger(NetBidibBonjourServiceRegistrationService.class);

    private List bonjourServices = new LinkedList<>();

    private final ServiceConfigProvider serviceConfigProvider;

    private final NetBidibSettingsInterface netBidibSettings;

    private final ApplicationEventPublisher applicationEventPublisher;

    private final ScheduledExecutorService serviceWorker =
        Executors
            .newScheduledThreadPool(1, new ThreadFactoryBuilder().setNameFormat("serviceWorkers-thread-%d").build());

    private final ScheduledExecutorService searchWorker =
        Executors
            .newScheduledThreadPool(1, new ThreadFactoryBuilder().setNameFormat("searchWorkers-thread-%d").build());

    private final List serviceListeners = new LinkedList<>();

    private ScheduledFuture registerFuture;

    // private ScheduledFuture searchFuture;

    public NetBidibBonjourServiceRegistrationService(final NetBidibSettingsInterface netBidibSettings,
        final ServiceConfigProvider serviceConfigProvider, final ApplicationEventPublisher applicationEventPublisher) {
        this.netBidibSettings = netBidibSettings;
        this.serviceConfigProvider = serviceConfigProvider;
        this.applicationEventPublisher = applicationEventPublisher;

        LOGGER.info("Create new instance of NetBidibBonjourServiceRegistrationService.");
    }

    @EventListener
    public void handleContextStart(final ApplicationStartedEvent ase) {
        LOGGER.info("The application is started.");
    }

    @EventListener
    public void handleContextStart(final WizardApplicationReadyEvent ase) {
        LOGGER.info("The application is ready. Check if we have to register the bonjour service.");

        try {
            final NetBidibSettings nbs = AopUtils.getTargetObject(netBidibSettings);

            nbs.addPropertyChangeListener(NetBidibSettings.PROPERTY_DISCOVERY_ENABLED, evt -> {

                boolean isDiscoveryEnabled = netBidibSettings.isDiscoveryEnabled();
                LOGGER.info("The discovery enabled flag has changed, isDiscoveryEnabled: {}", isDiscoveryEnabled);

                if (isDiscoveryEnabled) {
                    registerServicesAndSearch();
                }
                else {
                    // stopSearch();

                    // unregisterAllServices(true);

                    final Future future = unregisterAllServices(true);
                    try {
                        future.get(5000, TimeUnit.MILLISECONDS);

                        LOGGER.info("Unregister all services has passed.");
                    }
                    catch (InterruptedException | ExecutionException | TimeoutException ex) {
                        LOGGER.warn("Wait for unregister services failed.", ex);
                    }
                }

            });

            nbs.addPropertyChangeListener(NetBidibSettings.PROPERTY_JMDNS_IP_ADDRESS, evt -> {

                LOGGER.info("Change of fix IP address was notified.");

                try {
                    if (netBidibSettings.isUseFixIpAddress() && netBidibSettings.isDiscoveryEnabled()) {
                        LOGGER.info("Use fix IP address and discovery is enabled.");

                        if (this.registeredFixAddress != null
                            && StringUtils.isNotBlank(netBidibSettings.getJmDnsIpAddress())
                            && !this.registeredFixAddress
                                .equals(InetAddress.getByName(netBidibSettings.getJmDnsIpAddress()))) {
                            InetAddress inetAddress = InetAddress.getByName(netBidibSettings.getJmDnsIpAddress());
                            LOGGER.info("The fix IP address was changed.");

                            // TODO if this happens before the desktop is created we run into a problem
                            this.applicationEventPublisher
                                .publishEvent(new ConsoleMessageEvent(ConsoleColor.black,
                                    "Restart the bonjourService after change fixed IP for inetAddress: " + inetAddress,
                                    false));

                            // stopSearch();

                            // unregisterAllServices(true);
                            final Future future = unregisterAllServices(true);
                            try {
                                future.get(5000, TimeUnit.MILLISECONDS);

                                LOGGER.info("UNregister all services has passed.");
                            }
                            catch (InterruptedException | ExecutionException | TimeoutException ex) {
                                LOGGER.warn("Wait for unregister services failed.", ex);
                            }

                            LOGGER.info("Start the services again to use the new fixed IP.");

                            registerServicesAndSearch();
                        }
                    }
                }
                catch (Exception ex) {
                    LOGGER.warn("Stop and register bonjour service under new static IP failed.", ex);
                }

            });
        }
        catch (Exception ex) {
            LOGGER.warn("Register for changes of netBidib settings failed.", ex);
        }

        if (netBidibSettings.isDiscoveryEnabled()) {
            registerServicesAndSearch();
        }
    }

    private void registerServicesAndSearch() {

        LOGGER.info("Register bonjour service and start the search.");

        if (this.registerFuture != null) {
            LOGGER.info("Register is in progress already.");
            return;
        }

        LOGGER.info("Start the discovery service.");

        this.registerFuture = serviceWorker.schedule(() -> {

            registerService(serviceConfigProvider.getServiceName());

            int listenPortNetBidib = serviceConfigProvider.getListenPortNetBidib();

            LOGGER.info("Register the local service, listenPortNetBidib: {}", listenPortNetBidib);
            registerLocalService(serviceConfigProvider.getNetBidibUniqueId(), listenPortNetBidib);

            registerServiceListeners();

            // LOGGER.info("List services.");
            // final String serviceType = BONJOUR_SERVICETYPE + LOCAL_SUFFIX;
            // listServices(serviceType);

            this.registerFuture = null;
        }, 5, TimeUnit.SECONDS);
    }

    @PreDestroy
    public void shutdown() {

        // unregisterAllServices(false);
        final Future future = unregisterAllServices(false);
        try {
            future.get(5000, TimeUnit.MILLISECONDS);

            LOGGER.info("Unregister all services has passed.");
        }
        catch (InterruptedException | ExecutionException | TimeoutException ex) {
            LOGGER.warn("Wait for unregister services failed.", ex);
        }
    }

    private InetAddress registeredFixAddress;

    @Override
    public void registerService(final String serviceName) {
        LOGGER.info("Register the bonjour service, serviceName: {}", serviceName);

        try {

            String hostname = InetAddress.getLocalHost().getHostName();
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            LOGGER.info("Current hostname: {}, hostAddress: {}", hostname, hostAddress);

            // InetAddress[] addresses = new InetAddress[] { InetAddress.getLocalHost() };
            InetAddress[] addresses =
                new InetAddress[] { NetworkAddressHelper.getLocalAddressOfType(Inet4Address.class) };

            this.registeredFixAddress = null;

            if (this.netBidibSettings.isUseFixIpAddress()) {
                // use fix address from settings
                String ipAddr = this.netBidibSettings.getJmDnsIpAddress();
                LOGGER.info("The discovery uses the configured IP address: {}", ipAddr);
                addresses = new InetAddress[] { InetAddress.getByName(ipAddr) };

                this.registeredFixAddress = addresses[0];
            }
            else if (this.netBidibSettings.isDiscoveryOnAllNetworkInterfacesEnabled()) {
                LOGGER.info("The discovery on all network interfaces is enabled.");
                addresses = NetworkTopologyDiscovery.Factory.getInstance().getInetAddresses();
            }

            if (addresses != null && addresses.length > 0) {

                for (InetAddress inetAddress : addresses) {
                    LOGGER.info("Create the bonjourService for inetAddress: {}", inetAddress);

                    try {
                        final JmDNS bonjourService = JmDNS.create(inetAddress);

                        bonjourServices.add(bonjourService);

                        // TODO if this happens before the desktop is created we run into a problem
                        this.applicationEventPublisher
                            .publishEvent(new ConsoleMessageEvent(ConsoleColor.black,
                                "Added the bonjourService for inetAddress: " + inetAddress, false));
                    }
                    catch (Exception ex1) {
                        LOGGER.warn("Failed to create the bonjourService for inetAddress: {}", inetAddress, ex1);

                        // TODO if this happens before the desktop is created we run into a problem
                        this.applicationEventPublisher
                            .publishEvent(new ConsoleMessageEvent(ConsoleColor.red,
                                "Failed to create the bonjourService for inetAddress: " + inetAddress, true));
                    }
                }
            }
            else {
                LOGGER.error("No valid addresses found to register bonjourService.");
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Create bonjour service failed.", ex);
        }
    }

    @Override
    public void registerLocalService(final long uniqueId, int listenPortNetBidib) {

        String discoveryServiceName = netBidibSettings.getDiscoveryServiceName();

        long uid = NodeUtils.setHasSubNodesFunctions(uniqueId);

        LOGGER
            .info("Register the local service, hexUniqueId: {}, discoveryServiceName: {}, listenPortNetBidib: {}",
                ByteUtils.formatHexUniqueId(uid), discoveryServiceName, listenPortNetBidib);

        final Map properties = new HashMap<>();
        properties.put("bidib", "interface");

        properties.put("uid", ByteUtils.bytesToHex(ByteUtils.convertLongToUniqueId(uid), false));
        properties.put("prod", netBidibSettings.getDiscoveryDisplayName());
        properties.put("user", netBidibSettings.getJmDnsName());

        for (JmDNS bonjourService : this.bonjourServices) {

            final String serviceType = BONJOUR_SERVICETYPE + LOCAL_SUFFIX;

            // Register a service
            try {
                LOGGER.info("Current bonjourService: {}", bonjourService);

                ServiceInfo serviceInfo =
                    ServiceInfo
                        .create(serviceType, serviceConfigProvider.getServiceName(), listenPortNetBidib, 0, 0, true,
                            properties);

                LOGGER.info("Register the service: {}", serviceInfo);
                bonjourService.registerService(serviceInfo);

                // TODO if this happens before the desktop is created we run into a problem
                this.applicationEventPublisher
                    .publishEvent(new ConsoleMessageEvent(ConsoleColor.black,
                        "Registered local service at bonjourService with serviceType: " + serviceType, false));
            }
            catch (IOException ex) {
                LOGGER.warn("Register service failed.", ex);

                // TODO if this happens before the desktop is created we run into a problem
                this.applicationEventPublisher
                    .publishEvent(new ConsoleMessageEvent(ConsoleColor.red,
                        "Register local service at bonjourService failed with serviceType: " + serviceType, true));
            }
        }
    }

    public void registerServiceListeners() {
        LOGGER.info("Register the bonjour service listeners.");

        for (JmDNS bonjourService : this.bonjourServices) {

            // Register a service
            try {
                LOGGER.info("Current bonjourService: {}", bonjourService);

                LOGGER.info("Register all serviceListeners: {}", this.serviceListeners);

                final String serviceType = BONJOUR_SERVICETYPE + LOCAL_SUFFIX;

                for (BonjourServiceListener serviceListener : serviceListeners) {

                    LOGGER.info("Add the serviceListener: {}, serviceInfo.type: {}", serviceListener, serviceType);
                    // Add a service listener
                    bonjourService.addServiceListener(serviceType, serviceListener);

                    // TODO if this happens before the desktop is created we run into a problem
                    this.applicationEventPublisher
                        .publishEvent(new ConsoleMessageEvent(ConsoleColor.black,
                            "Added bonjourService listener for service type: " + serviceType, false));
                }

            }
            catch (Exception ex) {
                LOGGER.warn("Register service listener failed.", ex);
            }
        }
    }

    @Override
    public void listServices(final String searchTypeValue) {
        LOGGER.info("List services from jmDNS. Current searchTypeValue: {}", searchTypeValue);

        searchWorker.submit(() -> {

            LOGGER.info("List services.");
            for (JmDNS bonjourService : this.bonjourServices) {

                try {

                    if (bonjourService instanceof JmDNSImpl) {
                        JmDNSImpl impl = (JmDNSImpl) bonjourService;
                        LOGGER.info("Clear the cache of the bonjour service.");
                        impl.getCache().logCachedContent();
                        impl.getCache().clear();
                        LOGGER.info("Clear the cache of the bonjour service finished.");
                    }

                    // Add a service listener
                    ServiceInfo[] serviceInfos = bonjourService.list(searchTypeValue);
                    for (ServiceInfo info : serviceInfos) {
                        LOGGER.info("## Fetched service with name: {}, urls: {}", info.getName(), info.getURLs());

                        this.serviceListeners.forEach(listener -> {
                            listener.clearRegisteredServices();
                            listener.processServiceInfo(info);
                        });
                    }
                }
                catch (Exception ex) {
                    LOGGER.warn("List registered services failed.", ex);
                }
            }
        });
    }

    public void addServiceListener(BonjourServiceListener serviceListener) {

        LOGGER.info("Add service listener for bonjourServiceType: {}", BONJOUR_SERVICETYPE);

        this.serviceListeners.add(serviceListener);
    }

    public void removeServiceListener(BonjourServiceListener serviceListener) {

        LOGGER.info("Remove service listener for bonjourServiceType: {}", BONJOUR_SERVICETYPE);

        this.serviceListeners.remove(serviceListener);

        final String serviceType = BONJOUR_SERVICETYPE + LOCAL_SUFFIX;

        for (JmDNS bonjourService : this.bonjourServices) {
            // Remove a service listener
            bonjourService.removeServiceListener(serviceType, serviceListener);
        }
    }

    @Override
    public Future unregisterAllServices(boolean publishEvents) {
        LOGGER.info("Let the scheduler unregister all services, publishEvents: {}", publishEvents);

        final Future future = serviceWorker.submit(() -> {
            LOGGER.info("Start unregister all services.");

            final String serviceType = BONJOUR_SERVICETYPE + LOCAL_SUFFIX;

            for (JmDNS bonjourService : this.bonjourServices) {

                try {
                    for (BonjourServiceListener serviceListener : serviceListeners) {
                        // Remove a service listener
                        bonjourService.removeServiceListener(serviceType, serviceListener);

                        serviceListener.clearRegisteredServices();

                        if (publishEvents) {
                            try {
                                // TODO if this happens before the desktop is created we run into a problem
                                this.applicationEventPublisher
                                    .publishEvent(new ConsoleMessageEvent(ConsoleColor.black,
                                        "Removed bonjourService listener for service type: " + serviceType, false));
                            }
                            catch (Exception ex1) {
                                LOGGER.warn("Publish removal of service to console failed.", ex1);
                            }
                        }
                    }
                }
                catch (Exception ex) {
                    LOGGER.warn("Unregister all serviceListeners failed.", ex);
                }

                try {

                    // Unregister all services
                    bonjourService.unregisterAllServices();

                    if (publishEvents) {
                        try {
                            // TODO if this happens before the desktop is created we run into a problem
                            this.applicationEventPublisher
                                .publishEvent(
                                    new ConsoleMessageEvent(ConsoleColor.black, "Removed all bonjourServices.", false));
                        }
                        catch (Exception ex1) {
                            LOGGER.warn("Publish removal of all services to console failed.", ex1);
                        }
                    }
                }
                catch (Exception ex) {
                    LOGGER.warn("Unregister services failed.", ex);
                }
            }

            LOGGER.info("Clear the bonjour services.");
            this.bonjourServices.clear();

            LOGGER.info("Unregister all services has finished.");
        });
        return future;
    }

    @Override
    public void resolveService(String type, String name) {
        LOGGER.info("Resolve the service info, type: {}, name: {}", type, name);

        this.bonjourServices.get(0).requestServiceInfo(type, name);
    }

    @Override
    public ServiceInfo getServiceInfo(String type, String name, boolean persistent) {
        LOGGER.info("Get the service info, type: {}, name: {}, persistent: {}", type, name, persistent);

        final ServiceInfo serviceInfo = this.bonjourServices.get(0).getServiceInfo(type, name, persistent, 5000);

        LOGGER.info("Current serviceInfo: {}", serviceInfo);
        return serviceInfo;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy