]> git.basschouten.com Git - openhab-addons.git/blob
005b2809674745bbf27ea6ff4b52e8b16e4bbf41
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.Dictionary;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Hashtable;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.Set;
30
31 import javax.servlet.ServletException;
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.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;
43
44 /**
45  * The {@link WundergroundUpdateReceiverServlet} is responsible for receiving updates,and
46  * updating the matching channels.
47  *
48  * @author Daniel Demus - Initial contribution
49  */
50 @NonNullByDefault
51 public class WundergroundUpdateReceiverServlet extends BaseOpenHABServlet
52         implements WundergroundUpdateReceiverServletControls {
53
54     public static final String SERVLET_URL = "/weatherstation/updateweatherstation.php";
55     private static final long serialVersionUID = -5296703727081438023L;
56
57     private final Logger logger = LoggerFactory.getLogger(WundergroundUpdateReceiverServlet.class);
58     private final Map<String, WundergroundUpdateReceiverHandler> handlers = new HashMap<>();
59
60     private static final Object LOCK = new Object();
61     private final WundergroundUpdateReceiverDiscoveryService discoveryService;
62
63     private boolean active = false;
64     private String errorDetail = "";
65
66     public WundergroundUpdateReceiverServlet(HttpService httpService,
67             WundergroundUpdateReceiverDiscoveryService discoveryService) {
68         super(httpService);
69         this.discoveryService = discoveryService;
70     }
71
72     public boolean isActive() {
73         synchronized (LOCK) {
74             return this.active;
75         }
76     }
77
78     public String getErrorDetail() {
79         synchronized (LOCK) {
80             return this.errorDetail;
81         }
82     }
83
84     public Set<String> getStationIds() {
85         return this.handlers.keySet();
86     }
87
88     public void activate() {
89         activate(SERVLET_URL, httpService.createDefaultHttpContext());
90     }
91
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());
98                 return;
99             }
100             this.handlers.put(handler.getStationId(), handler);
101             errorDetail = "";
102             if (!isActive()) {
103                 activate();
104             }
105         }
106     }
107
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);
113             }
114             if (this.handlers.isEmpty() && !this.discoveryService.isBackgroundDiscoveryEnabled()) {
115                 deactivate();
116             }
117         }
118     }
119
120     public void deactivate() {
121         synchronized (LOCK) {
122             logger.debug("Stopping servlet {} at {}", getClass().getSimpleName(), SERVLET_URL);
123             try {
124                 super.deactivate(SERVLET_URL);
125             } catch (IllegalArgumentException ignored) {
126                 // SERVLET_URL is already unregistered
127             }
128             errorDetail = "";
129             active = false;
130         }
131     }
132
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()))
137                     .collect(toSet());
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);
143             });
144         }
145     }
146
147     public void dispose() {
148         synchronized (this.handlers) {
149             Set<String> stationIds = new HashSet<>(getStationIds());
150             stationIds.forEach(this::removeHandler);
151             deactivate();
152         }
153     }
154
155     @Override
156     protected void activate(String alias, HttpContext httpContext) {
157         synchronized (LOCK) {
158             try {
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);
162                 errorDetail = "";
163                 active = true;
164             } catch (NamespaceException e) {
165                 active = false;
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) {
169                 active = false;
170                 errorDetail = "Servlet couldn't be registered - " + e.getMessage();
171                 logger.warn("Error during servlet registration", e);
172             }
173         }
174     }
175
176     @Override
177     protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp) throws IOException {
178         if (!active) {
179             return;
180         }
181         if (req == null) {
182             return;
183         }
184         if (resp == null) {
185             return;
186         }
187         if (req.getRequestURI() == null) {
188             return;
189         }
190         logger.trace("doGet {}", req.getQueryString());
191
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);
199             }
200             handler.updateChannelStates(states);
201         }, () -> {
202             this.discoveryService.addUnhandledStationId(stationId, req.getParameterMap());
203         });
204
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");
212         writer.flush();
213         writer.close();
214     }
215
216     protected Map<String, WundergroundUpdateReceiverHandler> getHandlers() {
217         return Collections.unmodifiableMap(this.handlers);
218     }
219 }