[miele] Use framework's HTTP client (#12545)

* Refactor to use framework's Jetty HTTP client
* Do not try to parse HTML as JSON on failed HTTP calls
* Improve handler initialization log messages

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
Jacob Laursen 2022-03-30 07:03:46 +02:00 committed by GitHub
parent a2a5c2e6ff
commit 8c6534300a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 94 deletions

View File

@ -12,21 +12,19 @@
*/
package org.openhab.binding.miele.internal;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Random;
import java.util.zip.GZIPInputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,13 +45,15 @@ import com.google.gson.JsonParser;
@NonNullByDefault
public class MieleGatewayCommunicationController {
private final URL url;
private final URI uri;
private final Random rand = new Random();
private final Gson gson = new Gson();
private final Logger logger = LoggerFactory.getLogger(MieleGatewayCommunicationController.class);
private final HttpClient httpClient;
public MieleGatewayCommunicationController(String host) throws MalformedURLException {
url = new URL("http://" + host + "/remote/json-rpc");
public MieleGatewayCommunicationController(HttpClient httpClient, String host) throws URISyntaxException {
uri = new URI("http://" + host + "/remote/json-rpc");
this.httpClient = httpClient;
}
public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
@ -69,27 +69,43 @@ public class MieleGatewayCommunicationController {
public JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
JsonElement result = null;
JsonObject req = new JsonObject();
JsonObject requestBodyAsJson = new JsonObject();
int id = rand.nextInt(Integer.MAX_VALUE);
req.addProperty("jsonrpc", "2.0");
req.addProperty("id", id);
req.addProperty("method", methodName);
requestBodyAsJson.addProperty("jsonrpc", "2.0");
requestBodyAsJson.addProperty("id", id);
requestBodyAsJson.addProperty("method", methodName);
JsonArray params = new JsonArray();
for (Object o : args) {
params.add(gson.toJsonTree(o));
}
req.add("params", params);
requestBodyAsJson.add("params", params);
String requestBody = requestBodyAsJson.toString();
Request request = httpClient.newRequest(uri).method(HttpMethod.POST)
.content(new StringContentProvider(requestBody), "application/json");
String requestData = req.toString();
String responseData = null;
try {
responseData = post(url, Collections.emptyMap(), requestData);
} catch (IOException e) {
throw new MieleRpcException("Exception occurred while posting data", e);
final ContentResponse contentResponse = request.send();
final int httpStatus = contentResponse.getStatus();
if (httpStatus != 200) {
if (httpStatus == 503) {
throw new MieleRpcException("Gateway is temporarily unavailable");
}
throw new MieleRpcException("Unexpected HTTP status code " + httpStatus);
}
responseData = contentResponse.getContentAsString();
} catch (TimeoutException e) {
throw new MieleRpcException("Timeout when calling gateway", e);
} catch (ExecutionException e) {
throw new MieleRpcException("Failure when calling gateway", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new MieleRpcException("Interrupted while calling gateway", e);
}
logger.trace("The request '{}' yields '{}'", requestData, responseData);
logger.trace("The request '{}' yields '{}'", requestBody, responseData);
JsonObject parsedResponse = null;
try {
parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
@ -107,7 +123,7 @@ public class MieleGatewayCommunicationController {
String message = (o.has("message") ? o.get("message").getAsString() : null);
String data = (o.has("data")
? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
: null);
: "");
throw new MieleRpcException(
"Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
} else {
@ -122,63 +138,4 @@ public class MieleGatewayCommunicationController {
return result;
}
private String post(URL url, Map<String, String> headers, String data) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.addRequestProperty(entry.getKey(), entry.getValue());
}
connection.addRequestProperty("Accept-Encoding", "gzip");
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.connect();
OutputStream out = null;
try {
out = connection.getOutputStream();
out.write(data.getBytes());
out.flush();
int statusCode = connection.getResponseCode();
if (statusCode != HttpURLConnection.HTTP_OK) {
logger.debug("An unexpected status code was returned: '{}'", statusCode);
}
} finally {
if (out != null) {
out.close();
}
}
String responseEncoding = connection.getHeaderField("Content-Encoding");
responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
try {
in = connection.getInputStream();
if ("gzip".equalsIgnoreCase(responseEncoding)) {
in = new GZIPInputStream(in);
}
in = new BufferedInputStream(in);
byte[] buff = new byte[1024];
int n;
while ((n = in.read(buff)) > 0) {
bos.write(buff, 0, n);
}
bos.flush();
bos.close();
} finally {
if (in != null) {
in.close();
}
}
return bos.toString();
}
}

View File

@ -23,6 +23,7 @@ import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService;
import org.openhab.binding.miele.internal.handler.CoffeeMachineHandler;
import org.openhab.binding.miele.internal.handler.DishWasherHandler;
@ -39,6 +40,7 @@ import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
@ -57,6 +59,7 @@ import org.osgi.service.component.annotations.Reference;
* handlers.
*
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Refactored to use framework's HTTP client
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.miele")
@ -67,14 +70,17 @@ public class MieleHandlerFactory extends BaseThingHandlerFactory {
MieleApplianceHandler.SUPPORTED_THING_TYPES.stream())
.collect(Collectors.toSet());
private final HttpClient httpClient;
private final TranslationProvider i18nProvider;
private final LocaleProvider localeProvider;
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
@Activate
public MieleHandlerFactory(final @Reference TranslationProvider i18nProvider,
final @Reference LocaleProvider localeProvider, ComponentContext componentContext) {
public MieleHandlerFactory(@Reference final HttpClientFactory httpClientFactory,
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider,
ComponentContext componentContext) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.i18nProvider = i18nProvider;
this.localeProvider = localeProvider;
}
@ -102,7 +108,7 @@ public class MieleHandlerFactory extends BaseThingHandlerFactory {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing);
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing, httpClient);
registerApplianceDiscoveryService(handler);
return handler;
} else if (MieleApplianceHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {

View File

@ -127,7 +127,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
@Override
public void initialize() {
logger.debug("Initializing Miele appliance handler.");
logger.debug("Initializing handler for thing {}", getThing().getUID());
final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
if (applianceId == null || applianceId.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,

View File

@ -17,9 +17,9 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.IllformedLocaleException;
@ -39,6 +39,7 @@ import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.MieleGatewayCommunicationController;
import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
@ -87,7 +88,8 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
private boolean lastBridgeConnectionState = false;
private Gson gson = new Gson();
private final HttpClient httpClient;
private final Gson gson = new Gson();
private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication;
private Set<DiscoveryListener> discoveryListeners = ConcurrentHashMap.newKeySet();
@ -99,21 +101,22 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
private Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<>();
private Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<>();
public MieleBridgeHandler(Bridge bridge) {
public MieleBridgeHandler(Bridge bridge, HttpClient httpClient) {
super(bridge);
this.httpClient = httpClient;
}
@Override
public void initialize() {
logger.debug("Initializing the Miele bridge handler.");
logger.debug("Initializing handler for bridge {}", getThing().getUID());
if (!validateConfig(getConfig())) {
return;
}
try {
gatewayCommunication = new MieleGatewayCommunicationController((String) getConfig().get(HOST));
} catch (MalformedURLException e) {
gatewayCommunication = new MieleGatewayCommunicationController(httpClient, (String) getConfig().get(HOST));
} catch (URISyntaxException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
return;
}