]> git.basschouten.com Git - openhab-addons.git/blob
c74457aa4ffba9f6ed4e4cec75fa901fda48a2a2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.IOException;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20
21 import javax.xml.parsers.ParserConfigurationException;
22
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;
37
38 /**
39  * Client implementation for sending messages via XML-RPC to the Homematic server.
40  *
41  * @author Gerhard Riegler - Initial contribution
42  */
43 public class XmlRpcClient extends RpcClient<String> {
44     private final Logger logger = LoggerFactory.getLogger(XmlRpcClient.class);
45     private HttpClient httpClient;
46
47     public XmlRpcClient(HomematicConfig config, HttpClient httpClient) throws IOException {
48         super(config);
49         this.httpClient = httpClient;
50     }
51
52     @Override
53     public RpcRequest<String> createRpcRequest(String methodName) {
54         return new XmlRpcRequest(methodName);
55     }
56
57     /**
58      * Returns the XML-RPC url.
59      */
60     @Override
61     protected String getRpcCallbackUrl() {
62         return "http://" + config.getCallbackHost() + ":" + config.getXmlCallbackPort();
63     }
64
65     @Override
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);
69         }
70         IOException reason = new IOException();
71         for (int rpcRetryCounter = 1; rpcRetryCounter <= MAX_RPC_RETRY; rpcRetryCounter++) {
72             try {
73                 byte[] response = send(port, request);
74                 if (response.length == 0 && "setInstallMode".equals(request.getMethodName())) {
75                     return new Object[] {};
76                 }
77                 Object[] data = new XmlRpcResponse(new ByteArrayInputStream(response), config.getEncoding())
78                         .getResponseData();
79                 return new RpcResponseParser(request).parse(data);
80             } catch (UnknownRpcFailureException | UnknownParameterSetException ex) {
81                 throw ex;
82             } catch (SAXException | ParserConfigurationException ex) {
83                 throw new IOException(ex);
84             } catch (IOException ex) {
85                 reason = ex;
86                 // no retries for "init" request or if connection is refused
87                 if ("init".equals(request.getMethodName()) || ex.getCause() instanceof ExecutionException) {
88                     break;
89                 }
90                 logger.debug("XmlRpcMessage failed({}), sending message again {}/{}", ex.getMessage(), rpcRetryCounter,
91                         MAX_RPC_RETRY);
92             }
93         }
94         throw reason;
95     }
96
97     private byte[] send(int port, RpcRequest<String> request) throws IOException {
98         byte[] ret = new byte[0];
99         try {
100             BytesContentProvider content = new BytesContentProvider(
101                     request.createMessage().getBytes(config.getEncoding()));
102             String url = String.format("http://%s:%s", config.getGatewayAddress(), port);
103             if (port == config.getGroupPort()) {
104                 url += "/groups";
105             }
106             Request req = httpClient.POST(url).content(content).timeout(config.getTimeout(), TimeUnit.SECONDS)
107                     .header(HttpHeader.CONTENT_TYPE, "text/xml;charset=" + config.getEncoding());
108             FutureResponseListener listener = new FutureResponseListener(req, config.getBufferSize() * 1024);
109             req.send(listener);
110             ContentResponse response = listener.get(config.getTimeout(), TimeUnit.SECONDS);
111             ret = response.getContent();
112             if (ret == null || ret.length == 0) {
113                 throw new IOException("Received no data from the Homematic gateway");
114             }
115             if (logger.isTraceEnabled()) {
116                 String result = new String(ret, config.getEncoding());
117                 logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
118             }
119         } catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException e) {
120             throw new IOException(e);
121         }
122         return ret;
123     }
124 }