[openhabcloud] improved logging for troubleshooting purposes (#12430)
* [openhabcloud] Sensor UUID and secret in logging Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhab] More logging. Lambda callbacks Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Sensor short UUID/secret completely Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] pong milliseconds Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Log websecket factory HTTP calls and PACKETs (on connect) Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Sensor more secrets Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] abort connect on exceptional errors Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] remove dead code Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Socket.IO http logging only with TRACE level enabled Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] logging level tuning Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] fix typo sensored->censored Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] warn level logging (not info) when disconnected Signed-off-by: Sami Salonen <ssalonen@gmail.com> Signed-off-by: Sami Salonen <ssalonen@gmail.com>
This commit is contained in:
parent
89c73a0d81
commit
67843ffbfd
|
@ -43,11 +43,17 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import io.socket.backo.Backoff;
|
import io.socket.backo.Backoff;
|
||||||
import io.socket.client.IO;
|
import io.socket.client.IO;
|
||||||
|
import io.socket.client.IO.Options;
|
||||||
import io.socket.client.Manager;
|
import io.socket.client.Manager;
|
||||||
import io.socket.client.Socket;
|
import io.socket.client.Socket;
|
||||||
import io.socket.emitter.Emitter;
|
import io.socket.emitter.Emitter;
|
||||||
import io.socket.engineio.client.Transport;
|
import io.socket.engineio.client.Transport;
|
||||||
|
import io.socket.parser.Packet;
|
||||||
|
import io.socket.parser.Parser;
|
||||||
import io.socket.thread.EventThread;
|
import io.socket.thread.EventThread;
|
||||||
|
import okhttp3.OkHttpClient.Builder;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides communication between openHAB and the openHAB Cloud service.
|
* This class provides communication between openHAB and the openHAB Cloud service.
|
||||||
|
@ -157,159 +163,170 @@ public class CloudClient {
|
||||||
|
|
||||||
public void connect() {
|
public void connect() {
|
||||||
try {
|
try {
|
||||||
socket = IO.socket(baseURL);
|
Options options = new Options();
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
// When trace level logging is enabled, we activate further logging of HTTP calls
|
||||||
|
// of the Socket.IO library
|
||||||
|
Builder okHttpBuilder = new Builder();
|
||||||
|
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||||
|
loggingInterceptor.setLevel(Level.BASIC);
|
||||||
|
okHttpBuilder.addInterceptor(loggingInterceptor);
|
||||||
|
okHttpBuilder.addNetworkInterceptor(loggingInterceptor);
|
||||||
|
options.callFactory = okHttpBuilder.build();
|
||||||
|
options.webSocketFactory = okHttpBuilder.build();
|
||||||
|
}
|
||||||
|
socket = IO.socket(baseURL, options);
|
||||||
URL parsed = new URL(baseURL);
|
URL parsed = new URL(baseURL);
|
||||||
protocol = parsed.getProtocol();
|
protocol = parsed.getProtocol();
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
logger.error("Error creating Socket.IO: {}", e.getMessage());
|
logger.error("Error creating Socket.IO: {}", e.getMessage());
|
||||||
|
return;
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
logger.error("Error parsing baseURL to get protocol, assuming https. Error: {}", e.getMessage());
|
logger.error("Error parsing baseURL to get protocol, assuming https. Error: {}", e.getMessage());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() {
|
//
|
||||||
@Override
|
// socket manager events
|
||||||
public void call(Object... args) {
|
//
|
||||||
logger.trace("Manager.EVENT_TRANSPORT");
|
socket.io()//
|
||||||
Transport transport = (Transport) args[0];
|
.on(Manager.EVENT_TRANSPORT, args -> {
|
||||||
transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
|
logger.trace("Manager.EVENT_TRANSPORT");
|
||||||
@Override
|
Transport transport = (Transport) args[0];
|
||||||
public void call(Object... args) {
|
transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
|
||||||
logger.trace("Transport.EVENT_REQUEST_HEADERS");
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
public void call(Object... args) {
|
||||||
Map<String, List<String>> headers = (Map<String, List<String>>) args[0];
|
logger.trace("Transport.EVENT_REQUEST_HEADERS");
|
||||||
headers.put("uuid", List.of(uuid));
|
@SuppressWarnings("unchecked")
|
||||||
headers.put("secret", List.of(secret));
|
Map<String, List<String>> headers = (Map<String, List<String>>) args[0];
|
||||||
headers.put("openhabversion", List.of(OpenHAB.getVersion()));
|
headers.put("uuid", List.of(uuid));
|
||||||
headers.put("clientversion", List.of(CloudService.clientVersion));
|
headers.put("secret", List.of(secret));
|
||||||
headers.put("remoteaccess", List.of(((Boolean) remoteAccessEnabled).toString()));
|
headers.put("openhabversion", List.of(OpenHAB.getVersion()));
|
||||||
}
|
headers.put("clientversion", List.of(CloudService.clientVersion));
|
||||||
});
|
headers.put("remoteaccess", List.of(((Boolean) remoteAccessEnabled).toString()));
|
||||||
}
|
}
|
||||||
}).on(Manager.EVENT_CONNECT_ERROR, new Emitter.Listener() {
|
});
|
||||||
|
})//
|
||||||
@Override
|
.on(Manager.EVENT_CONNECT_ERROR, args -> {
|
||||||
public void call(Object... args) {
|
if (args.length > 0) {
|
||||||
if (args.length > 0) {
|
|
||||||
if (args[0] instanceof Exception) {
|
|
||||||
Exception e = (Exception) args[0];
|
|
||||||
logger.debug(
|
|
||||||
"Error connecting to the openHAB Cloud instance: {} {}. Should reconnect automatically.",
|
|
||||||
e.getClass().getSimpleName(), e.getMessage());
|
|
||||||
} else {
|
|
||||||
logger.debug(
|
|
||||||
"Error connecting to the openHAB Cloud instance: {}. Should reconnect automatically.",
|
|
||||||
args[0]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.debug("Error connecting to the openHAB Cloud instance. Should reconnect automatically.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
logger.debug("Socket.IO connected");
|
|
||||||
isConnected = true;
|
|
||||||
onConnect();
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_CONNECTING, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
logger.debug("Socket.IO connecting");
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_RECONNECTING, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
logger.debug("Socket.IO re-connecting (attempt {})", args[0]);
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_RECONNECT, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
logger.debug("Socket.IO re-connected successfully (attempt {})", args[0]);
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_RECONNECT_ERROR, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
if (args[0] instanceof Exception) {
|
|
||||||
Exception e = (Exception) args[0];
|
|
||||||
logger.debug("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(),
|
|
||||||
e.getMessage());
|
|
||||||
} else {
|
|
||||||
logger.debug("Socket.IO re-connect attempt error: {}", args[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_RECONNECT_FAILED, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
logger.debug("Socket.IO re-connect attempts failed. Stopping reconnection.");
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
if (args.length > 0) {
|
|
||||||
logger.debug("Socket.IO disconnected: {}", args[0]);
|
|
||||||
} else {
|
|
||||||
logger.debug("Socket.IO disconnected");
|
|
||||||
}
|
|
||||||
isConnected = false;
|
|
||||||
onDisconnect();
|
|
||||||
}
|
|
||||||
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
if (CloudClient.this.socket.connected()) {
|
|
||||||
if (logger.isDebugEnabled() && args.length > 0) {
|
|
||||||
logger.error("Error during communication: {}", args[0]);
|
|
||||||
} else {
|
|
||||||
logger.error("Error during communication");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We are not connected currently, manual reconnection is needed to keep trying to (re-)establish
|
|
||||||
// connection.
|
|
||||||
//
|
|
||||||
// Socket.IO 1.x java client: 'error' event is emitted from Socket on connection errors that are not
|
|
||||||
// retried, but also with error that are automatically retried. If we
|
|
||||||
//
|
|
||||||
// Note how this is different in Socket.IO 2.x java client, Socket emits 'connect_error' event.
|
|
||||||
// OBS: Don't get confused with Socket IO 2.x docs online, in 1.x connect_error is emitted also on
|
|
||||||
// errors that are retried by the library automatically!
|
|
||||||
long delay = reconnectBackoff.duration();
|
|
||||||
// Try reconnecting on connection errors
|
|
||||||
if (logger.isDebugEnabled() && args.length > 0) {
|
|
||||||
if (args[0] instanceof Exception) {
|
if (args[0] instanceof Exception) {
|
||||||
Exception e = (Exception) args[0];
|
Exception e = (Exception) args[0];
|
||||||
logger.error(
|
logger.debug(
|
||||||
"Error connecting to the openHAB Cloud instance: {} {}. Reconnecting after {} ms.",
|
"Error connecting to the openHAB Cloud instance: {} {}. Should reconnect automatically.",
|
||||||
e.getClass().getSimpleName(), e.getMessage(), delay);
|
e.getClass().getSimpleName(), e.getMessage());
|
||||||
} else {
|
} else {
|
||||||
logger.error(
|
logger.debug(
|
||||||
"Error connecting to the openHAB Cloud instance: {}. Reconnecting after {} ms.",
|
"Error connecting to the openHAB Cloud instance: {}. Should reconnect automatically.",
|
||||||
args[0], delay);
|
args[0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error("Error connecting to the openHAB Cloud instance. Reconnecting.");
|
logger.debug("Error connecting to the openHAB Cloud instance. Should reconnect automatically.");
|
||||||
}
|
}
|
||||||
socket.close();
|
})//
|
||||||
sleepSocketIO(delay);
|
.on(Manager.EVENT_OPEN, args -> logger.debug("Socket.IO OPEN"))//
|
||||||
socket.connect();
|
.on(Manager.EVENT_CLOSE, args -> logger.debug("Socket.IO CLOSE: {}", args[0]))//
|
||||||
}
|
.on(Manager.EVENT_PACKET, args -> {
|
||||||
}
|
int packetTypeIndex = -1;
|
||||||
}).on("request", new Emitter.Listener() {
|
String type = "<unexpected packet type>";
|
||||||
@Override
|
if (args.length == 1 && args[0] instanceof Packet<?>) {
|
||||||
public void call(Object... args) {
|
packetTypeIndex = ((Packet<?>) args[0]).type;
|
||||||
onEvent("request", (JSONObject) args[0]);
|
|
||||||
}
|
|
||||||
}).on("cancel", new Emitter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void call(Object... args) {
|
|
||||||
onEvent("cancel", (JSONObject) args[0]);
|
|
||||||
}
|
|
||||||
}).on("command", new Emitter.Listener() {
|
|
||||||
|
|
||||||
@Override
|
if (packetTypeIndex < Parser.types.length) {
|
||||||
public void call(Object... args) {
|
type = Parser.types[packetTypeIndex];
|
||||||
onEvent("command", (JSONObject) args[0]);
|
} else {
|
||||||
}
|
type = "<unknown type>";
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
logger.trace("Socket.IO Packet: {} ({})", type, packetTypeIndex);
|
||||||
|
})//
|
||||||
|
;
|
||||||
|
|
||||||
|
//
|
||||||
|
// socket events
|
||||||
|
//
|
||||||
|
socket.on(Socket.EVENT_CONNECT, args -> {
|
||||||
|
logger.debug("Socket.IO connected");
|
||||||
|
isConnected = true;
|
||||||
|
onConnect();
|
||||||
|
})//
|
||||||
|
.on(Socket.EVENT_CONNECTING, args -> logger.debug("Socket.IO connecting"))//
|
||||||
|
.on(Socket.EVENT_RECONNECTING, args -> logger.debug("Socket.IO re-connecting (attempt {})", args[0]))//
|
||||||
|
.on(Socket.EVENT_RECONNECT,
|
||||||
|
args -> logger.debug("Socket.IO re-connected successfully (attempt {})", args[0]))//
|
||||||
|
.on(Socket.EVENT_RECONNECT_ERROR, args -> {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.debug("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(),
|
||||||
|
e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.debug("Socket.IO re-connect attempt error: {}", args[0]);
|
||||||
|
}
|
||||||
|
})//
|
||||||
|
.on(Socket.EVENT_RECONNECT_FAILED,
|
||||||
|
args -> logger.debug("Socket.IO re-connect attempts failed. Stopping reconnection."))//
|
||||||
|
.on(Socket.EVENT_DISCONNECT, args -> {
|
||||||
|
if (args.length > 0) {
|
||||||
|
logger.warn("Socket.IO disconnected: {}", args[0]);
|
||||||
|
} else {
|
||||||
|
logger.warn("Socket.IO disconnected");
|
||||||
|
}
|
||||||
|
isConnected = false;
|
||||||
|
onDisconnect();
|
||||||
|
})//
|
||||||
|
.on(Socket.EVENT_ERROR, args -> {
|
||||||
|
if (CloudClient.this.socket.connected()) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.warn("Error during communication: {} {}", e.getClass().getSimpleName(),
|
||||||
|
e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.warn("Error during communication: {}", args[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("Error during communication");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are not connected currently, manual reconnection is needed to keep trying to
|
||||||
|
// (re-)establish
|
||||||
|
// connection.
|
||||||
|
//
|
||||||
|
// Socket.IO 1.x java client: 'error' event is emitted from Socket on connection errors that
|
||||||
|
// are not
|
||||||
|
// retried, but also with error that are automatically retried. If we
|
||||||
|
//
|
||||||
|
// Note how this is different in Socket.IO 2.x java client, Socket emits 'connect_error'
|
||||||
|
// event.
|
||||||
|
// OBS: Don't get confused with Socket IO 2.x docs online, in 1.x connect_error is emitted
|
||||||
|
// also on
|
||||||
|
// errors that are retried by the library automatically!
|
||||||
|
long delay = reconnectBackoff.duration();
|
||||||
|
// Try reconnecting on connection errors
|
||||||
|
if (args.length > 0) {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.warn(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {} {}. Reconnecting after {} ms.",
|
||||||
|
e.getClass().getSimpleName(), e.getMessage(), delay);
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {}. Reconnecting after {} ms.",
|
||||||
|
args[0], delay);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("Error connecting to the openHAB Cloud instance. Reconnecting.");
|
||||||
|
}
|
||||||
|
socket.close();
|
||||||
|
sleepSocketIO(delay);
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
})//
|
||||||
|
|
||||||
|
.on(Socket.EVENT_PING, args -> logger.debug("Socket.IO ping"))//
|
||||||
|
.on(Socket.EVENT_PONG, args -> logger.debug("Socket.IO pong: {} ms", args[0]))//
|
||||||
|
.on("request", args -> onEvent("request", (JSONObject) args[0]))//
|
||||||
|
.on("cancel", args -> onEvent("cancel", (JSONObject) args[0]))//
|
||||||
|
.on("command", args -> onEvent("command", (JSONObject) args[0]))//
|
||||||
|
;
|
||||||
socket.connect();
|
socket.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +335,8 @@ public class CloudClient {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void onConnect() {
|
public void onConnect() {
|
||||||
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid, this.localBaseUrl);
|
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", censored(this.uuid),
|
||||||
|
this.localBaseUrl);
|
||||||
reconnectBackoff.reset();
|
reconnectBackoff.reset();
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
}
|
}
|
||||||
|
@ -328,21 +346,13 @@ public class CloudClient {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void onDisconnect() {
|
public void onDisconnect() {
|
||||||
logger.info("Disconnected from the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid,
|
logger.info("Disconnected from the openHAB Cloud service (UUID = {}, base URL = {})", censored(this.uuid),
|
||||||
this.localBaseUrl);
|
this.localBaseUrl);
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
// And clean up the list of running requests
|
// And clean up the list of running requests
|
||||||
runningRequests.clear();
|
runningRequests.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback method for socket.io client which is called when an error occurs
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void onError(IOException error) {
|
|
||||||
logger.debug("{}", error.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback method for socket.io client which is called when a message is received
|
* Callback method for socket.io client which is called when a message is received
|
||||||
*/
|
*/
|
||||||
|
@ -684,4 +694,11 @@ public class CloudClient {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String censored(String secret) {
|
||||||
|
if (secret.length() < 4) {
|
||||||
|
return "*******";
|
||||||
|
}
|
||||||
|
return secret.substring(0, 2) + "..." + secret.substring(secret.length() - 2, secret.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,7 @@ public class CloudService implements ActionService, CloudClientListener, EventSu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("UUID = {}, secret = {}", InstanceUUID.get(), getSecret());
|
logger.debug("UUID = {}, secret = {}", censored(InstanceUUID.get()), censored(getSecret()));
|
||||||
|
|
||||||
if (cloudClient != null) {
|
if (cloudClient != null) {
|
||||||
cloudClient.shutdown();
|
cloudClient.shutdown();
|
||||||
|
@ -284,7 +284,7 @@ public class CloudService implements ActionService, CloudClientListener, EventSu
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
try {
|
try {
|
||||||
Files.writeString(file.toPath(), content, StandardCharsets.UTF_8);
|
Files.writeString(file.toPath(), content, StandardCharsets.UTF_8);
|
||||||
logger.debug("Created file '{}' with content '{}'", file.getAbsolutePath(), content);
|
logger.debug("Created file '{}' with content '{}'", file.getAbsolutePath(), censored(content));
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
logger.error("Couldn't create file '{}'.", file.getPath(), e);
|
logger.error("Couldn't create file '{}'.", file.getPath(), e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -311,16 +311,23 @@ public class CloudService implements ActionService, CloudClientListener, EventSu
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
newSecretString = randomString(20);
|
newSecretString = randomString(20);
|
||||||
logger.debug("New secret = {}", newSecretString);
|
logger.debug("New secret = {}", censored(newSecretString));
|
||||||
writeFile(file, newSecretString);
|
writeFile(file, newSecretString);
|
||||||
} else {
|
} else {
|
||||||
newSecretString = readFirstLine(file);
|
newSecretString = readFirstLine(file);
|
||||||
logger.debug("Using secret at '{}' with content '{}'", file.getAbsolutePath(), newSecretString);
|
logger.debug("Using secret at '{}' with content '{}'", file.getAbsolutePath(), censored(newSecretString));
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSecretString;
|
return newSecretString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String censored(String secret) {
|
||||||
|
if (secret.length() < 4) {
|
||||||
|
return "*******";
|
||||||
|
}
|
||||||
|
return secret.substring(0, 2) + "..." + secret.substring(secret.length() - 2, secret.length());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendCommand(String itemName, String commandString) {
|
public void sendCommand(String itemName, String commandString) {
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in New Issue