import static org.eclipse.jetty.http.HttpHeader.*;
import static org.eclipse.jetty.http.HttpMethod.*;
+import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerBase;
import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerForAuthorizationHeader;
+import org.openhab.binding.folderwatcher.internal.api.exception.APIException;
+import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
+import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
/**
* The {@link S3Actions} class contains AWS S3 API implementation.
private String awsAccessKey;
private String awsSecretKey;
- public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) {
+ public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) throws APIException {
this(httpClientFactory, bucketName, region, "", "");
}
public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region, String awsAccessKey,
- String awsSecretKey) {
+ String awsSecretKey) throws APIException {
this.httpClient = httpClientFactory.getCommonHttpClient();
try {
this.bucketUri = new URL("http://" + bucketName + ".s3." + region + ".amazonaws.com");
} catch (MalformedURLException e) {
- throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
+ throw new APIException("Unable to parse service endpoint: " + e.getMessage());
}
this.region = region;
this.awsAccessKey = awsAccessKey;
this.awsSecretKey = awsSecretKey;
}
- public List<String> listBucket(String prefix) throws Exception {
+ public List<String> listBucket(String prefix) throws APIException, AuthException {
Map<String, String> headers = new HashMap<String, String>();
Map<String, String> params = new HashMap<String, String>();
return listObjectsV2(prefix, headers, params);
}
private List<String> listObjectsV2(String prefix, Map<String, String> headers, Map<String, String> params)
- throws Exception {
+ throws APIException, AuthException {
params.put("list-type", "2");
params.put("prefix", prefix);
if (!awsAccessKey.isEmpty() || !awsSecretKey.isEmpty()) {
headers.put("x-amz-content-sha256", AWS4SignerBase.EMPTY_BODY_SHA256);
AWS4SignerForAuthorizationHeader signer = new AWS4SignerForAuthorizationHeader(this.bucketUri, "GET", "s3",
region);
- String authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256,
- awsAccessKey, awsSecretKey);
+ String authorization;
+ try {
+ authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256, awsAccessKey,
+ awsSecretKey);
+ } catch (HttpUtilException e) {
+ throw new AuthException(e);
+ }
headers.put("Authorization", authorization);
}
request.param(paramKey, params.get(paramKey));
}
- ContentResponse contentResponse = request.send();
+ ContentResponse contentResponse;
+ try {
+ contentResponse = request.send();
+ } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ throw new APIException(e);
+ }
+
if (contentResponse.getStatus() != 200) {
- throw new Exception("HTTP Response is not 200");
+ throw new APIException("HTTP Response is not 200");
}
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ DocumentBuilder docBuilder;
+ try {
+ docBuilder = docBuilderFactory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new APIException(e);
+ }
InputSource is = new InputSource(new StringReader(contentResponse.getContentAsString()));
- Document doc = docBuilder.parse(is);
+ Document doc;
+ try {
+ doc = docBuilder.parse(is);
+ } catch (SAXException | IOException e) {
+ throw new APIException(e);
+ }
NodeList nameNodesList = doc.getElementsByTagName("Key");
List<String> returnList = new ArrayList<>();
import javax.crypto.spec.SecretKeySpec;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
import org.openhab.binding.folderwatcher.internal.api.util.BinaryUtils;
+import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
import org.openhab.binding.folderwatcher.internal.api.util.HttpUtils;
/**
public static final String SCHEME = "AWS4";
public static final String ALGORITHM = "HMAC-SHA256";
public static final String TERMINATOR = "aws4_request";
- public static final String ISO8601BasicFormat = "yyyyMMdd'T'HHmmss'Z'";
- public static final String DateStringFormat = "yyyyMMdd";
+ public static final String ISO8601_BASIC_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
+ public static final String DATESTRING_FORMAT = "yyyyMMdd";
protected URL endpointUrl;
protected String httpMethod;
protected String serviceName;
this.serviceName = serviceName;
this.regionName = regionName;
- dateTimeFormat = new SimpleDateFormat(ISO8601BasicFormat);
+ dateTimeFormat = new SimpleDateFormat(ISO8601_BASIC_FORMAT);
dateTimeFormat.setTimeZone(new SimpleTimeZone(0, "UTC"));
- dateStampFormat = new SimpleDateFormat(DateStringFormat);
+ dateStampFormat = new SimpleDateFormat(DATESTRING_FORMAT);
dateStampFormat.setTimeZone(new SimpleTimeZone(0, "UTC"));
}
}
protected static String getCanonicalRequest(URL endpoint, String httpMethod, String queryParameters,
- String canonicalizedHeaderNames, String canonicalizedHeaders, String bodyHash) {
+ String canonicalizedHeaderNames, String canonicalizedHeaders, String bodyHash) throws HttpUtilException {
return httpMethod + "\n" + getCanonicalizedResourcePath(endpoint) + "\n" + queryParameters + "\n"
+ canonicalizedHeaders + "\n" + canonicalizedHeaderNames + "\n" + bodyHash;
}
- protected static String getCanonicalizedResourcePath(URL endpoint) {
+ protected static String getCanonicalizedResourcePath(URL endpoint) throws HttpUtilException {
if (endpoint == null) {
return "/";
}
}
}
- public static String getCanonicalizedQueryString(Map<String, String> parameters) {
+ public static String getCanonicalizedQueryString(Map<String, String> parameters) throws HttpUtilException {
if (parameters == null || parameters.isEmpty()) {
return "";
}
}
protected static String getStringToSign(String scheme, String algorithm, String dateTime, String scope,
- String canonicalRequest) {
+ String canonicalRequest) throws AuthException {
return scheme + "-" + algorithm + "\n" + dateTime + "\n" + scope + "\n"
+ BinaryUtils.toHex(hash(canonicalRequest));
}
- public static byte[] hash(String text) {
+ public static byte[] hash(String text) throws AuthException {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes("UTF-8"));
return md.digest();
} catch (Exception e) {
- throw new RuntimeException("Unable to compute hash while signing request: " + e.getMessage(), e);
+ throw new AuthException("Unable to compute hash while signing request: " + e.getMessage(), e);
}
}
- public static byte[] hash(byte[] data) {
+ public static byte[] hash(byte[] data) throws AuthException {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data);
return md.digest();
} catch (Exception e) {
- throw new RuntimeException("Unable to compute hash while signing request: " + e.getMessage(), e);
+ throw new AuthException("Unable to compute hash while signing request: " + e.getMessage(), e);
}
}
- protected static byte[] sign(String stringData, byte[] key, String algorithm) {
+ protected static byte[] sign(String stringData, byte[] key, String algorithm) throws AuthException {
try {
byte[] data = stringData.getBytes("UTF-8");
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data);
} catch (Exception e) {
- throw new RuntimeException("Unable to calculate a request signature: " + e.getMessage(), e);
+ throw new AuthException("Unable to calculate a request signature: " + e.getMessage(), e);
}
}
}
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.folderwatcher.internal.api.exception.AuthException;
import org.openhab.binding.folderwatcher.internal.api.util.BinaryUtils;
+import org.openhab.binding.folderwatcher.internal.api.util.HttpUtilException;
/**
* The {@link AWS4SignerForAuthorizationHeader} class contains methods for AWS S3 API authentication using HTTP(S)
}
public String computeSignature(Map<String, String> headers, Map<String, String> queryParameters, String bodyHash,
- String awsAccessKey, String awsSecretKey) {
+ String awsAccessKey, String awsSecretKey) throws AuthException, HttpUtilException {
Date now = new Date();
String dateTimeStamp = dateTimeFormat.format(now);
headers.put("x-amz-date", dateTimeStamp);
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.folderwatcher.internal.api.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link APIException} signal's there was a problem with interacting with the API
+ *
+ * @author Leo Siepel - initial contribution
+ *
+ */
+@NonNullByDefault
+public class APIException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public APIException(String message) {
+ super(message);
+ }
+
+ public APIException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public APIException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.folderwatcher.internal.api.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link AuthException} signal's there was a problem with authentication
+ *
+ * @author Leo Siepel - initial contribution
+ *
+ */
+@NonNullByDefault
+public class AuthException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public AuthException(String message) {
+ super(message);
+ }
+
+ public AuthException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AuthException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.folderwatcher.internal.api.util;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HttpUtilException} signal;s there was a problem with contacting the API
+ *
+ * @author Leo Siepel - initial contribution
+ *
+ */
+@NonNullByDefault
+public class HttpUtilException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public HttpUtilException(String message) {
+ super(message);
+ }
+
+ public HttpUtilException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public HttpUtilException(Throwable cause) {
+ super(cause);
+ }
+}
*/
@NonNullByDefault
public class HttpUtils {
- public static String urlEncode(String url, boolean keepPathSlash) {
+ public static String urlEncode(String url, boolean keepPathSlash) throws HttpUtilException {
String encoded;
try {
encoded = URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 encoding is not supported.", e);
+ throw new HttpUtilException("UTF-8 encoding is not supported.", e);
}
if (keepPathSlash) {
encoded = encoded.replace("%2F", "/");
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.folderwatcher.internal.api.S3Actions;
+import org.openhab.binding.folderwatcher.internal.api.exception.APIException;
import org.openhab.binding.folderwatcher.internal.common.WatcherCommon;
import org.openhab.binding.folderwatcher.internal.config.S3BucketWatcherConfiguration;
import org.openhab.core.OpenHAB;
@Override
public void initialize() {
config = getConfigAs(S3BucketWatcherConfiguration.class);
-
- if (config.s3Anonymous) {
- s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion);
- } else {
- s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion, config.awsKey,
- config.awsSecret);
+ try {
+ if (config.s3Anonymous) {
+ s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion);
+ } else {
+ s3 = new S3Actions(httpClientFactory, config.s3BucketName, config.awsRegion, config.awsKey,
+ config.awsSecret);
+ }
+ } catch (APIException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage());
+ return;
}
try {