2 * Copyright (c) 2010-2022 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.Dictionary;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Hashtable;
28 import java.util.Optional;
31 import javax.servlet.ServletException;
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.openhab.core.io.http.servlet.BaseOpenHABServlet;
38 import org.osgi.service.http.HttpContext;
39 import org.osgi.service.http.HttpService;
40 import org.osgi.service.http.NamespaceException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * The {@link WundergroundUpdateReceiverServlet} is responsible for receiving updates,and
46 * updating the matching channels.
48 * @author Daniel Demus - Initial contribution
51 public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
52 implements WundergroundUpdateReceiverServletControls {
54 public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php";
55 private static final long serialVersionUID = -5296703727081438023L;
57 private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
58 private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
60 private static final Object LOCK = new Object();
61 private final WundergroundUpdateReceiverDiscoveryService discoveryService;
63 private boolean active = false;
64 private String errorDetail = "";
66 public WundergroundUpdateReceiverServlet(HttpService httpService,
67 WundergroundUpdateReceiverDiscoveryService discoveryService) {
69 this.discoveryService = discoveryService;
72 public boolean isActive() {
78 public String getErrorDetail() {
80 return this.errorDetail;
84 public Set<String> getStationIds() {
85 return this.handlers.keySet();
88 public void activate() {
89 activate(SERVLET_URL, httpService.createDefaultHttpContext());
92 public void addHandler(WundergroundUpdateReceiverHandler handler) {
93 synchronized (this.handlers) {
94 if (this.handlers.containsKey(handler.getStationId())) {
95 errorDetail = "Handler handling request for stationId " + handler.getStationId() + " is already added";
96 logger.warn("Error during handler registration - StationId {} already being handled",
97 handler.getStationId());
100 this.handlers.put(handler.getStationId(), handler);
108 public void removeHandler(String stationId) {
109 synchronized (this.handlers) {
110 WundergroundUpdateReceiverHandler handler = this.handlers.get(stationId);
111 if (handler != null) {
112 this.handlers.remove(stationId);
114 if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) {
120 public void deactivate() {
121 synchronized (LOCK) {
122 logger.debug("Stopping servlet {} at {}", getClass().getSimpleName(), SERVLET_URL);
124 super.deactivate(SERVLET_URL);
125 } catch (IllegalArgumentException ignored) {
126 // SERVLET_URL is already unregistered
133 public void handlerConfigUpdated(WundergroundUpdateReceiverHandler handler) {
134 synchronized (this.handlers) {
135 final Set<Map.Entry<String, WundergroundUpdateReceiverHandler>> changedStationIds = this.handlers.entrySet()
136 .stream().filter(entry -> handler.getThing().getUID().equals(entry.getValue().getThing().getUID()))
138 changedStationIds.forEach(entry -> {
139 logger.debug("Re-assigning listener from station id {} to station id {}", entry.getKey(),
140 handler.getStationId());
141 this.removeHandler(entry.getKey());
142 this.addHandler(handler);
147 public void dispose() {
148 synchronized (this.handlers) {
149 Set<String> stationIds = new HashSet<>(getStationIds());
150 stationIds.forEach(this::removeHandler);
156 protected void activate(String alias, HttpContext httpContext) {
157 synchronized (LOCK) {
159 logger.debug("Starting servlet {} at {}", getClass().getSimpleName(), alias);
160 Dictionary<String, String> props = new Hashtable<>(1, 10);
161 httpService.registerServlet(alias, this, props, httpContext);
164 } catch (NamespaceException e) {
166 errorDetail = "Servlet couldn't be registered - alias " + alias + " already in use";
167 logger.warn("Error during servlet registration - alias {} already in use", alias, e);
168 } catch (ServletException e) {
170 errorDetail = "Servlet couldn't be registered - " + e.getMessage();
171 logger.warn("Error during servlet registration", e);
177 protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
187 if (req.getRequestURI() == null) {
190 logger.trace("doGet {}", req.getQueryString());
192 String stationId = req.getParameter(STATION_ID_PARAMETER);
193 Optional.ofNullable(this.handlers.get(stationId)).ifPresentOrElse(handler -> {
194 Map<String, String> states = req.getParameterMap().entrySet().stream()
195 .collect(toMap(Map.Entry::getKey, e -> String.join("", e.getValue())));
196 String queryString = req.getQueryString();
197 if (queryString != null && queryString.length() > 0) {
198 states.put(LAST_QUERY, queryString);
200 handler.updateChannelStates(states);
202 this.discoveryService.addUnhandledStationId(stationId, req.getParameterMap());
205 resp.setStatus(HttpServletResponse.SC_OK);
206 resp.setContentType("text/html;charset=utf-8");
207 resp.setContentLength(7);
208 resp.setDateHeader("Date", Instant.now().toEpochMilli());
209 resp.setHeader("Connection", "close");
210 PrintWriter writer = resp.getWriter();
211 writer.write("success");
216 protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
217 return Collections.unmodifiableMap(this.handlers);