Greatly simplify Network discovery code

use jmmdns to simplyfy the code and automatically take care of interfaces switching
This commit is contained in:
Martino Facchin 2016-02-15 17:12:35 +01:00
parent aab908086e
commit b2241dadf8
5 changed files with 65 additions and 311 deletions

View File

@ -31,89 +31,38 @@ package cc.arduino.packages.discoverers;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Discovery;
import cc.arduino.packages.discoverers.network.BoardReachabilityFilter;
import cc.arduino.packages.discoverers.network.NetworkChecker;
import org.apache.commons.compress.utils.IOUtils;
import processing.app.BaseNoGui;
import processing.app.zeroconf.jmdns.ArduinoDNSTaskStarter;
import javax.jmdns.*;
import javax.jmdns.impl.DNSTaskStarter;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;
public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.packages.discoverers.network.NetworkTopologyListener {
import cc.arduino.packages.discoverers.network.BoardReachabilityFilter;
public class NetworkDiscovery implements Discovery, ServiceListener {
private static final int MAX_TIME_AWAITING_FOR_PACKAGES = 5000;
private final List<BoardPort> boardPortsDiscoveredWithJmDNS;
private final Map<InetAddress, JmDNS> mappedJmDNSs;
private Timer networkCheckerTimer;
private Timer boardReachabilityFilterTimer;
private final List<BoardPort> reachableBoardPorts;
private final List<BoardPort> reachableBoardPorts = new LinkedList<>();
private final List<BoardPort> boardPortsDiscoveredWithJmDNS = new LinkedList<>();
private Timer reachabilityTimer;
private JmmDNS jmdns = null;
public NetworkDiscovery() {
DNSTaskStarter.Factory.setClassDelegate(new ArduinoDNSTaskStarter());
this.boardPortsDiscoveredWithJmDNS = new LinkedList<>();
this.mappedJmDNSs = new Hashtable<>();
this.reachableBoardPorts = new LinkedList<>();
}
@Override
public List<BoardPort> listDiscoveredBoards() {
synchronized (reachableBoardPorts) {
return new LinkedList<>(reachableBoardPorts);
}
}
@Override
public List<BoardPort> listDiscoveredBoards(boolean complete) {
synchronized (reachableBoardPorts) {
return new LinkedList<>(reachableBoardPorts);
}
}
public void setReachableBoardPorts(List<BoardPort> newReachableBoardPorts) {
synchronized (reachableBoardPorts) {
this.reachableBoardPorts.clear();
this.reachableBoardPorts.addAll(newReachableBoardPorts);
}
}
public List<BoardPort> getBoardPortsDiscoveredWithJmDNS() {
private void removeDuplicateBoards(BoardPort newBoard) {
synchronized (boardPortsDiscoveredWithJmDNS) {
return new LinkedList<>(boardPortsDiscoveredWithJmDNS);
Iterator<BoardPort> iterator = boardPortsDiscoveredWithJmDNS.iterator();
while (iterator.hasNext()) {
BoardPort board = iterator.next();
if (newBoard.getAddress().equals(board.getAddress())) {
iterator.remove();
}
}
}
}
@Override
public void start() throws IOException {
this.networkCheckerTimer = new Timer(NetworkChecker.class.getName());
new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance()).start(networkCheckerTimer);
this.boardReachabilityFilterTimer = new Timer(BoardReachabilityFilter.class.getName());
new BoardReachabilityFilter(this).start(boardReachabilityFilterTimer);
}
@Override
public void stop() throws IOException {
this.networkCheckerTimer.purge();
this.boardReachabilityFilterTimer.purge();
// we don't close each JmDNS instance as it's too slow
}
@Override
public void serviceAdded(ServiceEvent serviceEvent) {
String type = serviceEvent.getType();
String name = serviceEvent.getName();
JmDNS dns = serviceEvent.getDNS();
dns.requestServiceInfo(type, name);
ServiceInfo serviceInfo = dns.getServiceInfo(type, name);
if (serviceInfo != null) {
dns.requestServiceInfo(type, name);
}
}
@Override
@ -126,11 +75,9 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
@Override
public void serviceResolved(ServiceEvent serviceEvent) {
int sleptFor = 0;
while (BaseNoGui.packages == null && sleptFor <= MAX_TIME_AWAITING_FOR_PACKAGES) {
while (BaseNoGui.packages == null) {
try {
Thread.sleep(1000);
sleptFor += 1000;
} catch (InterruptedException e) {
e.printStackTrace();
}
@ -151,7 +98,7 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
port.getPrefs().put("board", board);
port.getPrefs().put("distro_version", info.getPropertyString("distro_version"));
port.getPrefs().put("port", "" + info.getPort());
//Add additional fields to permit generic ota updates
//and make sure we do not intefere with Arduino boards
// define "ssh_upload=no" TXT property to use generic uploader
@ -190,35 +137,56 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
}
}
private void removeDuplicateBoards(BoardPort newBoard) {
synchronized (boardPortsDiscoveredWithJmDNS) {
Iterator<BoardPort> iterator = boardPortsDiscoveredWithJmDNS.iterator();
while (iterator.hasNext()) {
BoardPort board = iterator.next();
if (newBoard.getAddress().equals(board.getAddress())) {
iterator.remove();
}
}
}
public NetworkDiscovery() {
}
@Override
public void inetAddressAdded(InetAddress address) {
if (mappedJmDNSs.containsKey(address)) {
return;
}
public void start() {
jmdns = JmmDNS.Factory.getInstance();
jmdns.addServiceListener("_arduino._tcp.local.", this);
reachabilityTimer = new Timer();
new BoardReachabilityFilter(this).start(reachabilityTimer);
}
@Override
public void stop() {
jmdns.unregisterAllServices();
// we don't close the JmmDNS instance as it's too slow
/*
try {
JmDNS jmDNS = JmDNS.create(address);
jmDNS.addServiceListener("_arduino._tcp.local.", this);
mappedJmDNSs.put(address, jmDNS);
} catch (Exception e) {
jmdns.close();
} catch (IOException e) {
e.printStackTrace();
}
*/
reachabilityTimer.cancel();
}
@Override
public void inetAddressRemoved(InetAddress address) {
JmDNS jmDNS = mappedJmDNSs.remove(address);
IOUtils.closeQuietly(jmDNS);
public List<BoardPort> listDiscoveredBoards() {
synchronized (reachableBoardPorts) {
return new LinkedList<>(reachableBoardPorts);
}
}
@Override
public List<BoardPort> listDiscoveredBoards(boolean complete) {
synchronized (reachableBoardPorts) {
return new LinkedList<>(reachableBoardPorts);
}
}
public void setReachableBoardPorts(List<BoardPort> newReachableBoardPorts) {
synchronized (reachableBoardPorts) {
this.reachableBoardPorts.clear();
this.reachableBoardPorts.addAll(newReachableBoardPorts);
}
}
public List<BoardPort> getBoardPortsDiscoveredWithJmDNS() {
synchronized (boardPortsDiscoveredWithJmDNS) {
return new LinkedList<>(boardPortsDiscoveredWithJmDNS);
}
}
}

View File

@ -34,23 +34,28 @@ import cc.arduino.packages.discoverers.NetworkDiscovery;
import processing.app.helpers.NetUtils;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.net.UnknownHostException;
import java.util.*;
public class BoardReachabilityFilter extends TimerTask {
private final NetworkDiscovery networkDiscovery;
private Enumeration<NetworkInterface> staticNetworkInterfaces;
private final List<String> staticNetworkInterfacesList = new LinkedList<>();
public BoardReachabilityFilter(NetworkDiscovery networkDiscovery) {
this.networkDiscovery = networkDiscovery;
}
public void start(Timer timer) {
timer.schedule(this, 0, 3000);
timer.schedule(this, 0, 5000);
}
@Override
public void run() {
List<BoardPort> boardPorts = networkDiscovery.getBoardPortsDiscoveredWithJmDNS();
Iterator<BoardPort> boardPortIterator = boardPorts.iterator();

View File

@ -1,84 +0,0 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers.network;
import javax.jmdns.NetworkTopologyDiscovery;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.*;
public class NetworkChecker extends TimerTask {
private final NetworkTopologyListener topologyListener;
private final NetworkTopologyDiscovery topology;
private Set<InetAddress> knownAddresses;
public NetworkChecker(NetworkTopologyListener topologyListener, NetworkTopologyDiscovery topology) {
super();
this.topologyListener = topologyListener;
this.topology = topology;
this.knownAddresses = Collections.synchronizedSet(new HashSet<>());
}
public void start(Timer timer) {
timer.schedule(this, 0, 3000);
}
@Override
public void run() {
if (!hasNetworkInterfaces()) {
return;
}
try {
InetAddress[] curentAddresses = topology.getInetAddresses();
Set<InetAddress> current = new HashSet<>(curentAddresses.length);
for (InetAddress address : curentAddresses) {
current.add(address);
if (!knownAddresses.contains(address)) {
topologyListener.inetAddressAdded(address);
}
}
knownAddresses.stream().filter(address -> !current.contains(address)).forEach(topologyListener::inetAddressRemoved);
knownAddresses = current;
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean hasNetworkInterfaces() {
try {
return NetworkInterface.getNetworkInterfaces() != null;
} catch (SocketException e) {
return false;
}
}
}

View File

@ -1,40 +0,0 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers.network;
import java.net.InetAddress;
public interface NetworkTopologyListener {
void inetAddressAdded(InetAddress address);
void inetAddressRemoved(InetAddress address);
}

View File

@ -1,95 +0,0 @@
package processing.app.zeroconf.jmdns;
import javax.jmdns.impl.DNSIncoming;
import javax.jmdns.impl.DNSTaskStarter;
import javax.jmdns.impl.JmDNSImpl;
import javax.jmdns.impl.ServiceInfoImpl;
import javax.jmdns.impl.tasks.RecordReaper;
import java.net.InetAddress;
import java.util.Timer;
public class ArduinoDNSTaskStarter implements DNSTaskStarter.Factory.ClassDelegate {
@Override
public DNSTaskStarter newDNSTaskStarter(final JmDNSImpl jmDNSImpl) {
final DNSTaskStarter.DNSTaskStarterImpl delegate = new DNSTaskStarter.DNSTaskStarterImpl(jmDNSImpl);
final DNSTaskStarter.DNSTaskStarterImpl.StarterTimer timer = new DNSTaskStarter.DNSTaskStarterImpl.StarterTimer("JmDNS(" + jmDNSImpl.getName() + ").Timer", true);
return new DNSTaskStarter() {
@Override
public void purgeTimer() {
delegate.purgeTimer();
timer.purge();
}
@Override
public void purgeStateTimer() {
delegate.purgeStateTimer();
}
@Override
public void cancelTimer() {
delegate.cancelTimer();
timer.cancel();
}
@Override
public void cancelStateTimer() {
delegate.cancelStateTimer();
}
@Override
public void startProber() {
delegate.startProber();
}
@Override
public void startAnnouncer() {
delegate.startAnnouncer();
}
@Override
public void startRenewer() {
delegate.startRenewer();
}
@Override
public void startCanceler() {
delegate.startCanceler();
}
@Override
public void startReaper() {
new RecordReaper(jmDNSImpl) {
@Override
public void start(Timer timer) {
if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
timer.schedule(this, 0, 500);
}
}
}.start(timer);
}
@Override
public void startServiceInfoResolver(ServiceInfoImpl info) {
delegate.startServiceInfoResolver(info);
}
@Override
public void startTypeResolver() {
delegate.startTypeResolver();
}
@Override
public void startServiceResolver(String type) {
delegate.startServiceResolver(type);
}
@Override
public void startResponder(DNSIncoming in, InetAddress addr, int port) {
delegate.startResponder(in, addr, port);
}
};
}
}