]> git.basschouten.com Git - openhab-addons.git/blob
db27828c6c0ab0607ae90dccce9ca78f8ac8f9f9
[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())
88                         || ex.getCause() != null && ex.getCause() instanceof ExecutionException) {
89                     break;
90                 }
91                 logger.debug("XmlRpcMessage failed({}), sending message again {}/{}", ex.getMessage(), rpcRetryCounter,
92                         MAX_RPC_RETRY);
93             }
94         }
95         throw reason;
96     }
97
98     private byte[] send(int port, RpcRequest<String> request) throws IOException {
99         byte[] ret = new byte[0];
100         try {
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()) {
105                 url += "/groups";
106             }
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);
110             req.send(listener);
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");
115             }
116             if (logger.isTraceEnabled()) {
117                 String result = new String(ret, config.getEncoding());
118                 logger.trace("Client XmlRpcResponse (port {}):\n{}", port, result);
119             }
120         } catch (InterruptedException | ExecutionException | TimeoutException | IllegalArgumentException e) {
121             throw new IOException(e);
122         }
123         return ret;
124     }
125 }