/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.io.transport.mdns.discovery; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceInfo; import javax.jmdns.ServiceListener; import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.mdns.MDNSClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a {@link DiscoveryService} implementation, which can find mDNS services in the network. Support for further * devices can be added by implementing and registering a {@link MDNSDiscoveryParticipant}. * * @author Tobias Bräutigam - Initial contribution * @author Kai Kreuzer - Improved startup behavior and background discovery * @author Andre Fuechsel - make {@link #startScan()} asynchronous */ public class MDNSDiscoveryService extends AbstractDiscoveryService implements ServiceListener { private final Logger logger = LoggerFactory.getLogger(MDNSDiscoveryService.class); private Set<MDNSDiscoveryParticipant> participants = new CopyOnWriteArraySet<>(); private MDNSClient mdnsClient; public MDNSDiscoveryService() { super(5); } public void setMDNSClient(MDNSClient mdnsClient) { this.mdnsClient = mdnsClient; if (isBackgroundDiscoveryEnabled()) { for (MDNSDiscoveryParticipant participant : participants) { mdnsClient.addServiceListener(participant.getServiceType(), this); } } } public void unsetMDNSClient(MDNSClient mdnsClient) { for (MDNSDiscoveryParticipant participant : participants) { mdnsClient.removeServiceListener(participant.getServiceType(), this); } this.mdnsClient = null; } @Override protected void startBackgroundDiscovery() { for (MDNSDiscoveryParticipant participant : participants) { mdnsClient.addServiceListener(participant.getServiceType(), this); } startScan(); } @Override protected void stopBackgroundDiscovery() { for (MDNSDiscoveryParticipant participant : participants) { mdnsClient.removeServiceListener(participant.getServiceType(), this); } } @Override protected void startScan() { scheduler.schedule(new Runnable() { @Override public void run() { scan(); } }, 0, TimeUnit.SECONDS); } private void scan() { for (MDNSDiscoveryParticipant participant : participants) { ServiceInfo[] services = mdnsClient.list(participant.getServiceType()); logger.debug("{} services found for {}", services.length, participant.getServiceType()); for (ServiceInfo service : services) { DiscoveryResult result = participant.createResult(service); if (result != null) { thingDiscovered(result); } } } } protected void addMdnsDiscoveryParticipant(MDNSDiscoveryParticipant participant) { this.participants.add(participant); if (mdnsClient != null && isBackgroundDiscoveryEnabled()) { mdnsClient.addServiceListener(participant.getServiceType(), this); } } protected void removeMdnsDiscoveryParticipant(MDNSDiscoveryParticipant participant) { this.participants.remove(participant); if (mdnsClient != null) { mdnsClient.removeServiceListener(participant.getServiceType(), this); } } @Override public Set<ThingTypeUID> getSupportedThingTypes() { Set<ThingTypeUID> supportedThingTypes = new HashSet<>(); for (MDNSDiscoveryParticipant participant : participants) { supportedThingTypes.addAll(participant.getSupportedThingTypeUIDs()); } return supportedThingTypes; } @Override public void serviceAdded(ServiceEvent serviceEvent) { considerService(serviceEvent); } @Override public void serviceRemoved(ServiceEvent serviceEvent) { for (MDNSDiscoveryParticipant participant : participants) { try { ThingUID thingUID = participant.getThingUID(serviceEvent.getInfo()); if (thingUID != null) { thingRemoved(thingUID); } } catch (Exception e) { logger.error("Participant '{}' threw an exception", participant.getClass().getName(), e); } } } @Override public void serviceResolved(ServiceEvent serviceEvent) { considerService(serviceEvent); } private void considerService(ServiceEvent serviceEvent) { if (isBackgroundDiscoveryEnabled()) { for (MDNSDiscoveryParticipant participant : participants) { try { DiscoveryResult result = participant.createResult(serviceEvent.getInfo()); if (result != null) { thingDiscovered(result); } } catch (Exception e) { logger.error("Participant '{}' threw an exception", participant.getClass().getName(), e); } } } } }