2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.amazondashbutton.internal.pcap;
15 import java.util.Collections;
16 import java.util.HashSet;
18 import java.util.concurrent.CopyOnWriteArraySet;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.stream.Collectors;
24 import org.openhab.core.common.ThreadPoolManager;
25 import org.pcap4j.core.PcapNetworkInterface;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * The {@link PcapNetworkInterfaceService} is a singleton which can be obtained by calling {@link #instance()}.
31 * It provides all available {@link PcapNetworkInterface}s which are bound to an address. These network interfaces can
32 * be retrieved by calling {@link #getNetworkInterfaces()}.
34 * Moreover the {@link PcapNetworkInterfaceService} provided the possibility to register a
35 * {@link PcapNetworkInterfaceListener}s which are notified on new and removed {@link PcapNetworkInterface}s.
37 * @author Oliver Libutzki - Initial contribution
40 public class PcapNetworkInterfaceService {
42 private final Logger logger = LoggerFactory.getLogger(PcapNetworkInterfaceService.class);
44 private static PcapNetworkInterfaceService instance = null;
45 private static final String THREADPOOL_NAME = "pcapNetworkInterfaceService";
47 private final Set<PcapNetworkInterfaceListener> listeners = new CopyOnWriteArraySet<>();
48 private final Set<PcapNetworkInterfaceWrapper> pcapNetworkInterfaces = new CopyOnWriteArraySet<>();
50 private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(THREADPOOL_NAME);
52 private ScheduledFuture<?> future = null;
54 private final Runnable pollingRunnable = () -> {
55 synchronized (pcapNetworkInterfaces) {
56 final Set<PcapNetworkInterfaceWrapper> determinedNetworkInterfaces = determineBoundNetworkInterfaces();
57 final Set<PcapNetworkInterfaceWrapper> currentNetworkInterfaces = new HashSet<>(pcapNetworkInterfaces);
59 final Set<PcapNetworkInterfaceWrapper> newNetworkInterfaces = new HashSet<>(determinedNetworkInterfaces);
60 newNetworkInterfaces.removeIf(currentNetworkInterfaces::contains);
62 final Set<PcapNetworkInterfaceWrapper> removedNetworkInterfaces = new HashSet<>(currentNetworkInterfaces);
63 removedNetworkInterfaces.removeIf(determinedNetworkInterfaces::contains);
65 pcapNetworkInterfaces.clear();
66 pcapNetworkInterfaces.addAll(determinedNetworkInterfaces);
68 for (PcapNetworkInterfaceWrapper pcapNetworkInterface : newNetworkInterfaces) {
69 notifyNetworkInterfacesAdded(pcapNetworkInterface);
72 for (PcapNetworkInterfaceWrapper pcapNetworkInterface : removedNetworkInterfaces) {
73 notifyNetworkInterfacesRemoved(pcapNetworkInterface);
78 private PcapNetworkInterfaceService() {
82 * Returns the {@link PcapNetworkInterfaceService} singleton instance.
84 * @return The {@link PcapNetworkInterfaceService} singleton
86 public static synchronized PcapNetworkInterfaceService instance() {
87 if (instance == null) {
88 instance = new PcapNetworkInterfaceService();
94 * Returns a {@link Set} of {@link PcapNetworkInterface}s which are bound to an address.
96 * @return the network interface set
98 public Set<PcapNetworkInterfaceWrapper> getNetworkInterfaces() {
99 synchronized (pcapNetworkInterfaces) {
100 return Collections.unmodifiableSet(pcapNetworkInterfaces.stream().collect(Collectors.toSet()));
105 * Registers the given {@link PcapNetworkInterfaceListener}. If it is already registered, this method returns
108 * @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be registered.
110 public void registerListener(PcapNetworkInterfaceListener networkInterfaceListener) {
111 final boolean isAdded = listeners.add(networkInterfaceListener);
113 updatePollingState();
118 * Unregisters the given {@link PcapNetworkInterfaceListener}. If it is already unregistered, this method returns
121 * @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be unregistered.
123 public void unregisterListener(PcapNetworkInterfaceListener networkInterfaceListener) {
124 final boolean isRemoved = listeners.remove(networkInterfaceListener);
126 updatePollingState();
130 private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
131 for (PcapNetworkInterfaceListener listener : listeners) {
132 notifyNetworkInterfacesAdded(listener, pcapNetworkInterface);
136 private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
137 for (PcapNetworkInterfaceListener listener : listeners) {
138 notifyNetworkInterfacesRemoved(listener, pcapNetworkInterface);
142 private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceListener listener,
143 PcapNetworkInterfaceWrapper pcapNetworkInterface) {
145 listener.onPcapNetworkInterfaceAdded(pcapNetworkInterface);
146 } catch (Exception e) {
147 logger.error("An exception occurred while calling onPcapNetworkInterfaceAdded for {}", listener, e);
151 private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceListener listener,
152 PcapNetworkInterfaceWrapper pcapNetworkInterface) {
154 listener.onPcapNetworkInterfaceRemoved(pcapNetworkInterface);
155 } catch (Exception e) {
156 logger.error("An exception occurred while calling onPcapNetworkInterfaceRemoved for {}", listener, e);
161 * Returns all pcap network interfaces relying on {@link PcapUtil#getAllNetworkInterfaces()}. The list is filtered
162 * as all interfaces which are not bound to an address are excluded.
164 * @return An {@link Iterable} of all {@link PcapNetworkInterfaceWrapper}s which are bound to an address
166 private Set<PcapNetworkInterfaceWrapper> determineBoundNetworkInterfaces() {
167 final Set<PcapNetworkInterfaceWrapper> allNetworkInterfaces = new HashSet<>();
168 PcapUtil.getAllNetworkInterfaces().forEach(allNetworkInterfaces::add);
169 allNetworkInterfaces.removeIf(networkInterface -> {
170 final boolean notSuitable = networkInterface.getAddresses().isEmpty();
172 logger.debug("{} is not a suitable network interfaces as no addresses are bound to it.",
173 networkInterface.getName());
177 return allNetworkInterfaces;
180 private void updatePollingState() {
181 boolean isPolling = future != null;
182 if (isPolling && listeners.isEmpty()) {
187 if (!isPolling && !listeners.isEmpty()) {
188 future = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, 2, TimeUnit.SECONDS);