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.homematic.internal.communicator.client;
15 import java.io.ByteArrayInputStream;
16 import java.io.IOException;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
21 import javax.xml.parsers.ParserConfigurationException;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.api.ContentResponse;
25 import org.eclipse.jetty.client.api.Request;
26 import org.eclipse.jetty.client.util.BytesContentProvider;
27 import org.eclipse.jetty.client.util.FutureResponseListener;
28 import org.eclipse.jetty.http.HttpHeader;
29 import org.openhab.binding.homematic.internal.common.HomematicConfig;
30 import org.openhab.binding.homematic.internal.communicator.message.RpcRequest;
31 import org.openhab.binding.homematic.internal.communicator.message.XmlRpcRequest;
32 import org.openhab.binding.homematic.internal.communicator.message.XmlRpcResponse;
33 import org.openhab.binding.homematic.internal.communicator.parser.RpcResponseParser;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.xml.sax.SAXException;
39 * Client implementation for sending messages via XML-RPC to the Homematic server.
41 * @author Gerhard Riegler - Initial contribution
43 public class XmlRpcClient extends RpcClient<String> {
44 private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
45 private HttpClient httpClient;
47 public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException {
49 this.httpClient = httpClient;
53 public RpcRequest<String> createRpcRequest(String methodName) {
54 return new XmlRpcRequest(methodName);
58 * Returns the XML-RPC url.
61 protected String getRpcCallbackUrl() {
62 return "http://" + config.getCallbackHost() + ":" + config.getXmlCallbackPort();
66 protected synchronized Object[] sendMessage(int port, RpcRequest<String> request) throws IOException {
67 if (logger.isTraceEnabled()) {
68 logger.trace("Client XmlRpcRequest (port {}):\n{}", port, request);
70 IOException reason = new IOException();
71 for (int rpcRetryCounter = 1; rpcRetryCounter <= MAX_RPC_RETRY; rpcRetryCounter++) {
73 byte[] response = send(port, request);
74 if (response.length == 0 && "setInstallMode".equals(request.getMethodName())) {
75 return new Object[] {};
77 Object[] data = new XmlRpcResponse(new ByteArrayInputStream(response), config.getEncoding())
79 return new RpcResponseParser(request).parse(data);
80 } catch (UnknownRpcFailureException | UnknownParameterSetException ex) {
82 } catch (SAXException | ParserConfigurationException ex) {
83 throw new IOException(ex);
84 } catch (IOException ex) {
86 // no retries for "init" request or if connection is refused
87 if ("init".equals(request.getMethodName())
88 || ex.getCause() != null && ex.getCause() instanceof ExecutionException) {
91 logger.debug("XmlRpcMessage failed({}), sending message again {}/{}", ex.getMessage(), rpcRetryCounter,
98 private byte[] send(int port, RpcRequest<String> request) throws IOException {
99 byte[] ret = new byte[0];
101 BytesContentProvider content = new BytesContentProvider(
102 request.createMessage().getBytes(config.getEncoding()));
103 String url = String.format("http://%s:%s", config.getGatewayAddress(), port);
104 if (port == config.getGroupPort()) {
107 Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
108 .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
109 FutureResponseListener listener = new FutureResponseListener(req, config.getBufferSize() * 1024);
111 ContentResponse response = listener.get(config.getTimeout(), TimeUnit.SECONDS);
112 ret = response.getContent();
113 if (ret == null || ret.length == 0) {
114 throw new IOException("Received no data from the Homematic gateway");
116 if (logger.isTraceEnabled()) {
117 String result = new String(ret, config.getEncoding());
118 logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
120 } catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException e) {
121 throw new IOException(e);