2 * Copyright (c) 2010-2021 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.homematic.internal.communicator.client;
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;
24 import javax.xml.parsers.ParserConfigurationException;
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;
43 * Client implementation for sending messages via XML-RPC to the Homematic server.
45 * @author Gerhard Riegler - Initial contribution
47 public class XmlRpcClient extends RpcClient<String> {
48 private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
49 private HttpClient httpClient;
51 public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException {
53 this.httpClient = httpClient;
57 public void dispose() {
61 public RpcRequest<String> createRpcRequest(String methodName) {
62 return new XmlRpcRequest(methodName);
66 * Returns the XML-RPC url.
69 protected String getRpcCallbackUrl() {
70 return "http://" + config.getCallbackHost() + ":" + config.getXmlCallbackPort();
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);
78 IOException reason = new IOException();
79 for (int rpcRetryCounter = 1; rpcRetryCounter <= MAX_RPC_RETRY; rpcRetryCounter++) {
81 byte[] response = send(port, request);
82 if (response.length == 0 && "setInstallMode".equals(request.getMethodName())) {
83 return new Object[] {};
85 Object[] data = new XmlRpcResponse(new ByteArrayInputStream(response), config.getEncoding())
87 return new RpcResponseParser(request).parse(data);
88 } catch (UnknownRpcFailureException | UnknownParameterSetException ex) {
90 } catch (SAXException | ParserConfigurationException ex) {
91 throw new IOException(ex);
92 } catch (IOException ex) {
94 if ("init".equals(request.getMethodName())) { // no retries for "init" request
97 logger.debug("XmlRpcMessage failed, sending message again {}/{}", rpcRetryCounter, MAX_RPC_RETRY);
103 private byte[] send(int port, RpcRequest<String> request) throws IOException {
104 byte[] ret = new byte[0];
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()) {
112 Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
113 .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
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);
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()) {
128 int read = input.read(recvBuffer);
132 respData.write(recvBuffer, 0, read);
134 ret = respData.toByteArray();
137 logger.warn("Non-blocking XmlRpcRequest failed, status code: {} / request: {}", httpStatus,
139 resp.abort(new IOException());
142 if (logger.isTraceEnabled()) {
143 String result = new String(ret, config.getEncoding());
144 logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
146 } catch (UnsupportedEncodingException | ExecutionException | TimeoutException | InterruptedException e) {
147 throw new IOException(e);