001package jmri.util.zeroconf; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.List; 006import javax.annotation.Nonnull; 007import javax.jmdns.JmDNS; 008import javax.jmdns.NetworkTopologyEvent; 009import javax.jmdns.NetworkTopologyListener; 010import javax.jmdns.ServiceEvent; 011import javax.jmdns.ServiceInfo; 012import javax.jmdns.ServiceListener; 013import javax.jmdns.impl.constants.DNSConstants; 014import jmri.InstanceManager; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018public class ZeroConfClient { 019 020 private ServiceListener mdnsServiceListener = null; 021 private final static Logger log = LoggerFactory.getLogger(ZeroConfClient.class); 022 private long timeout = DNSConstants.SERVICE_INFO_TIMEOUT; 023 024 // mdns related routines. 025 public void startServiceListener(@Nonnull String service) { 026 log.debug("StartServiceListener called for service: {}", service); 027 if (mdnsServiceListener == null) { 028 mdnsServiceListener = new NetworkServiceListener(service, this); 029 } 030 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 031 server.addServiceListener(service, mdnsServiceListener); 032 } 033 } 034 035 public void stopServiceListener(@Nonnull String service) { 036 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 037 server.removeServiceListener(service, mdnsServiceListener); 038 } 039 } 040 041 /** 042 * Request the first service of a particular service. 043 * 044 * @param service string service getName 045 * @return JmDNS service entry for the first service of a particular 046 * service. 047 */ 048 public ServiceInfo getService(@Nonnull String service) { 049 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 050 ServiceInfo[] infos = server.list(service, timeout); 051 if (infos != null) { 052 return infos[0]; 053 } 054 } 055 return null; 056 } 057 058 /** 059 * Get all servers providing the specified service. 060 * 061 * @param service the name of service as generated using 062 * {@link jmri.util.zeroconf.ZeroConfServiceManager#key(java.lang.String, java.lang.String) } 063 * @return A list of servers or an empty list. 064 */ 065 @Nonnull 066 public List<ServiceInfo> getServices(@Nonnull String service) { 067 ArrayList<ServiceInfo> services = new ArrayList<>(); 068 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 069 ServiceInfo[] infos = server.list(service, timeout); 070 if (infos != null) { 071 services.addAll(Arrays.asList(infos)); 072 } 073 } 074 return services; 075 } 076 077 /** 078 * Request the first service of a particular service on a specified host. 079 * 080 * @param service string service service 081 * @param hostname string host name 082 * @return JmDNS service entry for the first service of a particular service 083 * on the specified host.. 084 */ 085 public ServiceInfo getServiceOnHost(@Nonnull String service, @Nonnull String hostname) { 086 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 087 ServiceInfo[] infos = server.list(service, timeout); 088 for (ServiceInfo info : infos) { 089 if (info.getServer().equals(hostname)) { 090 return info; 091 } 092 } 093 } 094 return null; 095 } 096 097 /** 098 * Request the first service of a particular service with a particular 099 * service name. 100 * 101 * @param service string service service 102 * @param adName string qualified service advertisement name 103 * @return JmDNS service entry for the first service of a particular service 104 * on the specified host.. 105 */ 106 public ServiceInfo getServicebyAdName(@Nonnull String service, @Nonnull String adName) { 107 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 108 ServiceInfo[] infos = server.list(service, timeout); 109 for (ServiceInfo info : infos) { 110 log.debug("Found Name: {}", info.getQualifiedName()); 111 if (info.getQualifiedName().equals(adName)) { 112 return info; 113 } 114 } 115 } 116 return null; 117 } 118 119 @Nonnull 120 public String[] getHostList(@Nonnull String service) { 121 ArrayList<String> hostlist = new ArrayList<>(); 122 for (JmDNS server : InstanceManager.getDefault(ZeroConfServiceManager.class).getDNSes().values()) { 123 ServiceInfo[] infos = server.list(service, timeout); 124 for (ServiceInfo info : infos) { 125 hostlist.add(info.getServer()); 126 } 127 } 128 return hostlist.toArray(new String[hostlist.size()]); 129 } 130 131 /** 132 * Get current timeout 133 * 134 * @return timeout as long in milliseconds 135 */ 136 public long getTimeout() { 137 return timeout; 138 } 139 140 /** 141 * Set timeout 142 * 143 * @param timeout in milliseconds 144 */ 145 public void setTimeout(long timeout) { 146 this.timeout = timeout; 147 } 148 149 public static class NetworkServiceListener implements ServiceListener, NetworkTopologyListener { 150 151 private final String service; 152 private final ZeroConfClient client; 153 154 protected NetworkServiceListener(String service, ZeroConfClient client) { 155 this.service = service; 156 this.client = client; 157 } 158 159 @Override 160 public void inetAddressAdded(NetworkTopologyEvent nte) { 161 nte.getDNS().addServiceListener(service, this); 162 } 163 164 @Override 165 public void inetAddressRemoved(NetworkTopologyEvent nte) { 166 nte.getDNS().removeServiceListener(service, this); 167 } 168 169 @Override 170 public void serviceAdded(ServiceEvent se) { 171 log.debug("Service added: {}", se.getInfo().toString()); 172 // notify the client when a service is added. 173 synchronized (client) { 174 client.notifyAll(); 175 } 176 } 177 178 @Override 179 public void serviceRemoved(ServiceEvent se) { 180 log.debug("Service removed: {}", se.getInfo().toString()); 181 } 182 183 @Override 184 public void serviceResolved(ServiceEvent se) { 185 log.debug("Service resolved: {}", se.getInfo().toString()); 186 } 187 188 } 189}