]> git.basschouten.com Git - openhab-addons.git/blob
15678fefbf7a951485d4cb0f18816493e49dc90a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.amazondashbutton.internal.pcap;
14
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.Set;
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;
23
24 import org.openhab.core.common.ThreadPoolManager;
25 import org.pcap4j.core.PcapNetworkInterface;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
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()}.
33  *
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.
36  *
37  * @author Oliver Libutzki - Initial contribution
38  *
39  */
40 public class PcapNetworkInterfaceService {
41
42     private final Logger logger = LoggerFactory.getLogger(PcapNetworkInterfaceService.class);
43
44     private static PcapNetworkInterfaceService instance = null;
45     private static final String THREADPOOL_NAME = "pcapNetworkInterfaceService";
46
47     private final Set<PcapNetworkInterfaceListener> listeners = new CopyOnWriteArraySet<>();
48     private final Set<PcapNetworkInterfaceWrapper> pcapNetworkInterfaces = new CopyOnWriteArraySet<>();
49
50     private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(THREADPOOL_NAME);
51
52     private ScheduledFuture<?> future = null;
53
54     private final Runnable pollingRunnable = () -> {
55         synchronized (pcapNetworkInterfaces) {
56             final Set<PcapNetworkInterfaceWrapper> determinedNetworkInterfaces = determineBoundNetworkInterfaces();
57             final Set<PcapNetworkInterfaceWrapper> currentNetworkInterfaces = new HashSet<>(pcapNetworkInterfaces);
58
59             final Set<PcapNetworkInterfaceWrapper> newNetworkInterfaces = new HashSet<>(determinedNetworkInterfaces);
60             newNetworkInterfaces.removeIf(currentNetworkInterfaces::contains);
61
62             final Set<PcapNetworkInterfaceWrapper> removedNetworkInterfaces = new HashSet<>(currentNetworkInterfaces);
63             removedNetworkInterfaces.removeIf(determinedNetworkInterfaces::contains);
64
65             pcapNetworkInterfaces.clear();
66             pcapNetworkInterfaces.addAll(determinedNetworkInterfaces);
67
68             for (PcapNetworkInterfaceWrapper pcapNetworkInterface : newNetworkInterfaces) {
69                 notifyNetworkInterfacesAdded(pcapNetworkInterface);
70             }
71
72             for (PcapNetworkInterfaceWrapper pcapNetworkInterface : removedNetworkInterfaces) {
73                 notifyNetworkInterfacesRemoved(pcapNetworkInterface);
74             }
75         }
76     };
77
78     private PcapNetworkInterfaceService() {
79     }
80
81     /**
82      * Returns the {@link PcapNetworkInterfaceService} singleton instance.
83      *
84      * @return The {@link PcapNetworkInterfaceService} singleton
85      */
86     public static synchronized PcapNetworkInterfaceService instance() {
87         if (instance == null) {
88             instance = new PcapNetworkInterfaceService();
89         }
90         return instance;
91     }
92
93     /**
94      * Returns a {@link Set} of {@link PcapNetworkInterface}s which are bound to an address.
95      *
96      * @return the network interface set
97      */
98     public Set<PcapNetworkInterfaceWrapper> getNetworkInterfaces() {
99         synchronized (pcapNetworkInterfaces) {
100             return Collections.unmodifiableSet(pcapNetworkInterfaces.stream().collect(Collectors.toSet()));
101         }
102     }
103
104     /**
105      * Registers the given {@link PcapNetworkInterfaceListener}. If it is already registered, this method returns
106      * immediately.
107      *
108      * @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be registered.
109      */
110     public void registerListener(PcapNetworkInterfaceListener networkInterfaceListener) {
111         final boolean isAdded = listeners.add(networkInterfaceListener);
112         if (isAdded) {
113             updatePollingState();
114         }
115     }
116
117     /**
118      * Unregisters the given {@link PcapNetworkInterfaceListener}. If it is already unregistered, this method returns
119      * immediately.
120      *
121      * @param networkInterfaceListener The {@link PcapNetworkInterfaceListener} to be unregistered.
122      */
123     public void unregisterListener(PcapNetworkInterfaceListener networkInterfaceListener) {
124         final boolean isRemoved = listeners.remove(networkInterfaceListener);
125         if (isRemoved) {
126             updatePollingState();
127         }
128     }
129
130     private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
131         for (PcapNetworkInterfaceListener listener : listeners) {
132             notifyNetworkInterfacesAdded(listener, pcapNetworkInterface);
133         }
134     }
135
136     private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceWrapper pcapNetworkInterface) {
137         for (PcapNetworkInterfaceListener listener : listeners) {
138             notifyNetworkInterfacesRemoved(listener, pcapNetworkInterface);
139         }
140     }
141
142     private void notifyNetworkInterfacesAdded(PcapNetworkInterfaceListener listener,
143             PcapNetworkInterfaceWrapper pcapNetworkInterface) {
144         try {
145             listener.onPcapNetworkInterfaceAdded(pcapNetworkInterface);
146         } catch (Exception e) {
147             logger.error("An exception occurred while calling onPcapNetworkInterfaceAdded for {}", listener, e);
148         }
149     }
150
151     private void notifyNetworkInterfacesRemoved(PcapNetworkInterfaceListener listener,
152             PcapNetworkInterfaceWrapper pcapNetworkInterface) {
153         try {
154             listener.onPcapNetworkInterfaceRemoved(pcapNetworkInterface);
155         } catch (Exception e) {
156             logger.error("An exception occurred while calling onPcapNetworkInterfaceRemoved for {}", listener, e);
157         }
158     }
159
160     /**
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.
163      *
164      * @return An {@link Iterable} of all {@link PcapNetworkInterfaceWrapper}s which are bound to an address
165      */
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();
171             if (notSuitable) {
172                 logger.debug("{} is not a suitable network interfaces as no addresses are bound to it.",
173                         networkInterface.getName());
174             }
175             return notSuitable;
176         });
177         return allNetworkInterfaces;
178     }
179
180     private void updatePollingState() {
181         boolean isPolling = future != null;
182         if (isPolling && listeners.isEmpty()) {
183             future.cancel(true);
184             future = null;
185             return;
186         }
187         if (!isPolling && !listeners.isEmpty()) {
188             future = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, 2, TimeUnit.SECONDS);
189         }
190     }
191 }