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.wundergroundupdatereceiver.internal;
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.*;
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;
26 import java.util.Optional;
28 import java.util.regex.Pattern;
30 import javax.servlet.Servlet;
31 import javax.servlet.http.HttpServlet;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
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;
47 * The {@link WundergroundUpdateReceiverServlet} is responsible for receiving updates,and
48 * updating the matching channels.
50 * @author Daniel Demus - Initial contribution
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 {
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-]");
63 private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
64 private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
66 private static final Object LOCK = new Object();
67 private final WundergroundUpdateReceiverDiscoveryService discoveryService;
69 private boolean active = false;
70 private String errorDetail = "";
73 public WundergroundUpdateReceiverServlet(
74 final @Reference WundergroundUpdateReceiverDiscoveryService discoveryService) {
75 this.discoveryService = discoveryService;
77 active = discoveryService.isBackgroundDiscoveryEnabled();
80 public boolean isActive() {
86 public String getErrorDetail() {
88 return this.errorDetail;
92 public Set<String> getStationIds() {
93 return this.handlers.keySet();
96 public void addHandler(WundergroundUpdateReceiverHandler handler) {
97 synchronized (this.handlers) {
98 if (this.handlers.containsKey(handler.getStationId())) {
99 errorDetail = "Handler handling request for stationId " + handler.getStationId() + " is already added";
100 logger.warn("Error during handler registration - StationId {} already being handled",
101 handler.getStationId());
104 this.handlers.put(handler.getStationId(), handler);
112 public void removeHandler(String stationId) {
113 synchronized (this.handlers) {
114 WundergroundUpdateReceiverHandler handler = this.handlers.get(stationId);
115 if (handler != null) {
116 this.handlers.remove(stationId);
118 if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) {
125 public void enable() {
130 public void disable() {
135 public void handlerConfigUpdated(WundergroundUpdateReceiverHandler handler) {
136 synchronized (this.handlers) {
137 final Set<Map.Entry<String, WundergroundUpdateReceiverHandler>> changedStationIds = this.handlers.entrySet()
138 .stream().filter(entry -> handler.getThing().getUID().equals(entry.getValue().getThing().getUID()))
140 changedStationIds.forEach(entry -> {
141 logger.debug("Re-assigning listener from station id {} to station id {}", entry.getKey(),
142 handler.getStationId());
143 this.removeHandler(entry.getKey());
144 this.addHandler(handler);
149 public void dispose() {
150 synchronized (this.handlers) {
151 Set<String> stationIds = new HashSet<>(getStationIds());
152 stationIds.forEach(this::removeHandler);
157 protected Map<String, String> normalizeParameterMap(Map<String, String[]> parameterMap) {
158 return parameterMap.entrySet().stream()
159 .collect(toMap(e -> makeUidSafeString(e.getKey()), e -> String.join("", e.getValue())));
163 protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
173 if (req.getRequestURI() == null) {
176 logger.trace("doGet {}", req.getQueryString());
178 String stationId = req.getParameter(STATION_ID_PARAMETER);
179 Map<String, String> states = normalizeParameterMap(req.getParameterMap());
180 Optional.ofNullable(this.handlers.get(stationId)).ifPresentOrElse(handler -> {
181 String queryString = req.getQueryString();
182 if (queryString != null && queryString.length() > 0) {
183 states.put(LAST_QUERY, queryString);
185 handler.updateChannelStates(states);
187 this.discoveryService.addUnhandledStationId(stationId, states);
190 resp.setStatus(HttpServletResponse.SC_OK);
191 resp.setContentType("text/html;charset=utf-8");
192 resp.setContentLength(7);
193 resp.setDateHeader("Date", Instant.now().toEpochMilli());
194 resp.setHeader("Connection", "close");
195 PrintWriter writer = resp.getWriter();
196 writer.write("success");
201 protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
202 return Collections.unmodifiableMap(this.handlers);
205 private String makeUidSafeString(String key) {
206 return CLEANER.matcher(key).replaceAll("-");