]> git.basschouten.com Git - openhab-addons.git/blob
cf1e431c143e565ba1c82482400fc81ed4006ecb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.homematic.internal.communicator.client;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.UnsupportedEncodingException;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23
24 import javax.xml.parsers.ParserConfigurationException;
25
26 import org.eclipse.jetty.client.HttpClient;
27 import org.eclipse.jetty.client.api.Request;
28 import org.eclipse.jetty.client.api.Response;
29 import org.eclipse.jetty.client.util.BytesContentProvider;
30 import org.eclipse.jetty.client.util.InputStreamResponseListener;
31 import org.eclipse.jetty.http.HttpHeader;
32 import org.eclipse.jetty.http.HttpStatus;
33 import org.openhab.binding.homematic.internal.common.HomematicConfig;
34 import org.openhab.binding.homematic.internal.communicator.message.RpcRequest;
35 import org.openhab.binding.homematic.internal.communicator.message.XmlRpcRequest;
36 import org.openhab.binding.homematic.internal.communicator.message.XmlRpcResponse;
37 import org.openhab.binding.homematic.internal.communicator.parser.RpcResponseParser;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.xml.sax.SAXException;
41
42 /**
43  * Client implementation for sending messages via XML-RPC to the Homematic server.
44  *
45  * @author Gerhard Riegler - Initial contribution
46  */
47 public class XmlRpcClient extends RpcClient<String> {
48     private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
49     private HttpClient httpClient;
50
51     public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException {
52         super(config);
53         this.httpClient = httpClient;
54     }
55
56     @Override
57     public void dispose() {
58     }
59
60     @Override
61     public RpcRequest<String> createRpcRequest(String methodName) {
62         return new XmlRpcRequest(methodName);
63     }
64
65     /**
66      * Returns the XML-RPC url.
67      */
68     @Override
69     protected String getRpcCallbackUrl() {
70         return "http://" + config.getCallbackHost() + ":" + config.getXmlCallbackPort();
71     }
72
73     @Override
74     protected synchronized Object[] sendMessage(int port, RpcRequest<String> request) throws IOException {
75         if (logger.isTraceEnabled()) {
76             logger.trace("Client XmlRpcRequest (port {}):\n{}", port, request);
77         }
78         IOException reason = new IOException();
79         for (int rpcRetryCounter = 1; rpcRetryCounter <= MAX_RPC_RETRY; rpcRetryCounter++) {
80             try {
81                 byte[] response = send(port, request);
82                 if (response.length == 0 && "setInstallMode".equals(request.getMethodName())) {
83                     return new Object[] {};
84                 }
85                 Object[] data = new XmlRpcResponse(new ByteArrayInputStream(response), config.getEncoding())
86                         .getResponseData();
87                 return new RpcResponseParser(request).parse(data);
88             } catch (UnknownRpcFailureException | UnknownParameterSetException ex) {
89                 throw ex;
90             } catch (SAXException | ParserConfigurationException ex) {
91                 throw new IOException(ex);
92             } catch (IOException ex) {
93                 reason = ex;
94                 if ("init".equals(request.getMethodName())) { // no retries for "init" request
95                     break;
96                 }
97                 logger.debug("XmlRpcMessage failed, sending message again {}/{}", rpcRetryCounter, MAX_RPC_RETRY);
98             }
99         }
100         throw reason;
101     }
102
103     private byte[] send(int port, RpcRequest<String> request) throws IOException {
104         byte[] ret = new byte[0];
105         try {
106             BytesContentProvider content = new BytesContentProvider(
107                     request.createMessage().getBytes(config.getEncoding()));
108             String url = String.format("http://%s:%s", config.getGatewayAddress(), port);
109             if (port == config.getGroupPort()) {
110                 url += "/groups";
111             }
112             Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
113                     .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
114             try {
115                 ret = req.send().getContent();
116             } catch (IllegalArgumentException e) { // Returned buffer too large
117                 logger.info("Blocking XmlRpcRequest failed: {}, trying non-blocking request", e.getMessage());
118                 InputStreamResponseListener respListener = new InputStreamResponseListener();
119                 req.send(respListener);
120                 Response resp = respListener.get(config.getTimeout(), TimeUnit.SECONDS);
121                 ByteArrayOutputStream respData = new ByteArrayOutputStream(RESP_BUFFER_SIZE);
122
123                 int httpStatus = resp.getStatus();
124                 if (httpStatus == HttpStatus.OK_200) {
125                     byte[] recvBuffer = new byte[RESP_BUFFER_SIZE];
126                     try (InputStream input = respListener.getInputStream()) {
127                         while (true) {
128                             int read = input.read(recvBuffer);
129                             if (read == -1) {
130                                 break;
131                             }
132                             respData.write(recvBuffer, 0, read);
133                         }
134                         ret = respData.toByteArray();
135                     }
136                 } else {
137                     logger.warn("Non-blocking XmlRpcRequest failed, status code: {} / request: {}", httpStatus,
138                             request);
139                     resp.abort(new IOException());
140                 }
141             }
142             if (logger.isTraceEnabled()) {
143                 String result = new String(ret, config.getEncoding());
144                 logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
145             }
146         } catch (UnsupportedEncodingException | ExecutionException | TimeoutException | InterruptedException e) {
147             throw new IOException(e);
148         }
149         return ret;
150     }
151 }