/**
* 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.internal;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import org.eclipse.smarthome.io.transport.mdns.MDNSClient;
import org.eclipse.smarthome.io.transport.mdns.ServiceDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class starts the JmDNS and implements interface to register and unregister services.
*
* @author Victor Belov
*
*/
public class MDNSClientImpl implements MDNSClient {
private final Logger logger = LoggerFactory.getLogger(MDNSClientImpl.class);
private Set<JmDNS> jmdnsInstances = new CopyOnWriteArraySet<>();
private static Set<InetAddress> getAllInetAddresses() {
final Set<InetAddress> addresses = new HashSet<>();
Enumeration<NetworkInterface> itInterfaces;
try {
itInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (final SocketException e) {
return addresses;
}
while (itInterfaces.hasMoreElements()) {
final NetworkInterface iface = itInterfaces.nextElement();
try {
if (!iface.isUp() || iface.isLoopback()) {
continue;
}
} catch (final SocketException ex) {
continue;
}
final Enumeration<InetAddress> itAddresses = iface.getInetAddresses();
while (itAddresses.hasMoreElements()) {
final InetAddress address = itAddresses.nextElement();
if (address.isLoopbackAddress() || address.isLinkLocalAddress()) {
continue;
}
addresses.add(address);
}
}
return addresses;
}
/**
* {@inheritDoc}
*/
@Override
public Set<JmDNS> getClientInstances() {
return jmdnsInstances;
}
public void activate() {
for (InetAddress address : getAllInetAddresses()) {
try {
JmDNS jmdns = JmDNS.create(address, "JmDNS-IP-" + (jmdnsInstances.size() + 1));
jmdnsInstances.add(jmdns);
logger.debug("mDNS service has been started ({} for IP {})", jmdns.getName(), address.getHostAddress());
} catch (IOException e) {
logger.debug("JmDNS instanciation failed ({})!", address.getHostAddress());
}
}
if (jmdnsInstances.isEmpty()) {
// we must cancel the activation of this component here
throw new IllegalStateException("No mDNS service has been started");
}
}
public void deactivate() {
close();
}
/**
* {@inheritDoc}
*/
@Override
public void addServiceListener(String type, ServiceListener listener) {
for (JmDNS instance : jmdnsInstances) {
instance.addServiceListener(type, listener);
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeServiceListener(String type, ServiceListener listener) {
for (JmDNS instance : jmdnsInstances) {
instance.removeServiceListener(type, listener);
}
}
/**
* {@inheritDoc}
*/
@Override
public void registerService(ServiceDescription description) throws IOException {
for (JmDNS instance : jmdnsInstances) {
logger.debug("Registering new service {} at {}:{} ({})", description.serviceType,
instance.getInetAddress().getHostAddress(), description.servicePort, instance.getName());
// Create one ServiceInfo object for each JmDNS instance
ServiceInfo serviceInfo = ServiceInfo.create(description.serviceType, description.serviceName,
description.servicePort, 0, 0, description.serviceProperties);
instance.registerService(serviceInfo);
}
}
/**
* {@inheritDoc}
*/
@Override
public void unregisterService(ServiceDescription description) {
for (JmDNS instance : jmdnsInstances) {
try {
logger.debug("Unregistering service {} at {}:{} ({})", description.serviceType,
instance.getInetAddress().getHostAddress(), description.servicePort, instance.getName());
} catch (IOException e) {
logger.debug("Unregistering service {} ({})", description.serviceType, instance.getName());
}
ServiceInfo serviceInfo = ServiceInfo.create(description.serviceType, description.serviceName,
description.servicePort, 0, 0, description.serviceProperties);
instance.unregisterService(serviceInfo);
}
}
/**
* {@inheritDoc}
*/
@Override
public void unregisterAllServices() {
for (JmDNS instance : jmdnsInstances) {
instance.unregisterAllServices();
}
}
/**
* {@inheritDoc}
*/
@Override
public ServiceInfo[] list(String type) {
ServiceInfo[] services = new ServiceInfo[0];
for (JmDNS instance : jmdnsInstances) {
services = concatenate(services, instance.list(type));
}
return services;
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
for (JmDNS jmdns : jmdnsInstances) {
try {
jmdns.close();
} catch (IOException e) {
}
}
}
/**
* Concatenate two arrays of ServiceInfo
*
* @param a: the first array
* @param b: the second array
* @return an array of ServiceInfo
*/
private ServiceInfo[] concatenate(ServiceInfo[] a, ServiceInfo[] b) {
int aLen = a.length;
int bLen = b.length;
ServiceInfo[] c = new ServiceInfo[aLen + bLen];
System.arraycopy(a, 0, c, 0, aLen);
System.arraycopy(b, 0, c, aLen, bLen);
return c;
}
}