]> git.basschouten.com Git - openhab-addons.git/blob
116ee1f3df29eaf9f71b36b0700cdeab28891044
[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.wundergroundupdatereceiver.internal;
14
15 import static java.util.stream.Collectors.toMap;
16 import static java.util.stream.Collectors.toSet;
17 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
18
19 import java.io.IOException;
20 import java.io.PrintWriter;
21 import java.time.Instant;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.regex.Pattern;
29
30 import javax.servlet.Servlet;
31 import javax.servlet.http.HttpServlet;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34
35 import org.eclipse.jdt.annotation.NonNullByDefault;
36 import org.eclipse.jdt.annotation.Nullable;
37 import org.osgi.service.component.annotations.Activate;
38 import org.osgi.service.component.annotations.Component;
39 import org.osgi.service.component.annotations.Deactivate;
40 import org.osgi.service.component.annotations.Reference;
41 import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletName;
42 import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * The {@link WundergroundUpdateReceiverServlet} is responsible for receiving updates,and
48  * updating the matching channels.
49  *
50  * @author Daniel Demus - Initial contribution
51  */
52 @NonNullByDefault
53 @HttpWhiteboardServletName(WundergroundUpdateReceiverServlet.SERVLET_URL)
54 @HttpWhiteboardServletPattern(WundergroundUpdateReceiverServlet.SERVLET_URL)
55 @Component(immediate = true, service = { Servlet.class, WundergroundUpdateReceiverServlet.class })
56 public class WundergroundUpdateReceiverServlet extends HttpServlet
57         implements WundergroundUpdateReceiverServletControls {
58
59     public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php";
60     private static final long serialVersionUID = -5296703727081438023L;
61     private static final Pattern CLEANER = Pattern.compile("[^\\w-]");
62
63     private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
64     private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
65
66     private static final Object LOCK = new Object();
67     private final WundergroundUpdateReceiverDiscoveryService discoveryService;
68
69     private boolean active = false;
70     private String errorDetail = "";
71
72     @Activate
73     public WundergroundUpdateReceiverServlet(
74             final @Reference WundergroundUpdateReceiverDiscoveryService discoveryService) {
75         this.discoveryService = discoveryService;
76         errorDetail = "";
77         active = discoveryService.isBackgroundDiscoveryEnabled();
78     }
79
80     @Override
81     public boolean isActive() {
82         synchronized (LOCK) {
83             return this.active;
84         }
85     }
86
87     public String getErrorDetail() {
88         synchronized (LOCK) {
89             return this.errorDetail;
90         }
91     }
92
93     public Set<String> getStationIds() {
94         return this.handlers.keySet();
95     }
96
97     public void addHandler(WundergroundUpdateReceiverHandler handler) {
98         synchronized (this.handlers) {
99             if (this.handlers.containsKey(handler.getStationId())) {
100                 errorDetail = "Handler handling request for stationId " + handler.getStationId() + " is already added";
101                 logger.warn("Error during handler registration - StationId {} already being handled",
102                         handler.getStationId());
103                 return;
104             }
105             this.handlers.put(handler.getStationId(), handler);
106             errorDetail = "";
107             if (!isActive()) {
108                 enable();
109             }
110         }
111     }
112
113     public void removeHandler(String stationId) {
114         synchronized (this.handlers) {
115             WundergroundUpdateReceiverHandler handler = this.handlers.get(stationId);
116             if (handler != null) {
117                 this.handlers.remove(stationId);
118             }
119             if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) {
120                 disable();
121             }
122         }
123     }
124
125     @Override
126     public void enable() {
127         active = true;
128     }
129
130     @Deactivate
131     @Override
132     public void disable() {
133         errorDetail = "";
134         active = false;
135     }
136
137     public void handlerConfigUpdated(WundergroundUpdateReceiverHandler handler) {
138         synchronized (this.handlers) {
139             final Set<Map.Entry<String, WundergroundUpdateReceiverHandler>> changedStationIds = this.handlers.entrySet()
140                     .stream().filter(entry -> handler.getThing().getUID().equals(entry.getValue().getThing().getUID()))
141                     .collect(toSet());
142             changedStationIds.forEach(entry -> {
143                 logger.debug("Re-assigning listener from station id {} to station id {}", entry.getKey(),
144                         handler.getStationId());
145                 this.removeHandler(entry.getKey());
146                 this.addHandler(handler);
147             });
148         }
149     }
150
151     public void dispose() {
152         synchronized (this.handlers) {
153             Set<String> stationIds = new HashSet<>(getStationIds());
154             stationIds.forEach(this::removeHandler);
155             disable();
156         }
157     }
158
159     protected Map<String, String> normalizeParameterMap(Map<String, String[]> parameterMap) {
160         return parameterMap.entrySet().stream()
161                 .collect(toMap(e -> makeUidSafeString(e.getKey()), e -> String.join("", e.getValue())));
162     }
163
164     @Override
165     protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
166         if (!active) {
167             return;
168         }
169         if (req == null) {
170             return;
171         }
172         if (resp == null) {
173             return;
174         }
175         if (req.getRequestURI() == null) {
176             return;
177         }
178         logger.trace("doGet {}", req.getQueryString());
179
180         String stationId = req.getParameter(STATION_ID_PARAMETER);
181         Map<String, String> states = normalizeParameterMap(req.getParameterMap());
182         Optional.ofNullable(this.handlers.get(stationId)).ifPresentOrElse(handler -> {
183             String queryString = req.getQueryString();
184             if (queryString != null && queryString.length() > 0) {
185                 states.put(LAST_QUERY, queryString);
186             }
187             handler.updateChannelStates(states);
188         }, () -> {
189             this.discoveryService.addUnhandledStationId(stationId, states);
190         });
191
192         resp.setStatus(HttpServletResponse.SC_OK);
193         resp.setContentType("text/html;charset=utf-8");
194         resp.setContentLength(7);
195         resp.setDateHeader("Date", Instant.now().toEpochMilli());
196         resp.setHeader("Connection", "close");
197         PrintWriter writer = resp.getWriter();
198         writer.write("success");
199         writer.flush();
200         writer.close();
201     }
202
203     protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
204         return Collections.unmodifiableMap(this.handlers);
205     }
206
207     private String makeUidSafeString(String key) {
208         return CLEANER.matcher(key).replaceAll("-");
209     }
210 }