
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