diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 0000000..969e79a
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1,12 @@
+overrides:
+ - files:
+ - "**/*.java"
+ options:
+ trailingComma: none
+ useTabs: true
+ tabWidth: 2
+ semi: false
+ singleQuote: false
+ printWidth: 120
+ arrowParens: avoid
+ endOfLine: auto
\ No newline at end of file
diff --git a/checkstyle.origin.xml b/checkstyle.origin.xml
deleted file mode 100644
index 2098fe6..0000000
--- a/checkstyle.origin.xml
+++ /dev/null
@@ -1,222 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/checkstyle.xml b/checkstyle.xml
index 6f36b51..889d74d 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -1,195 +1,179 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 82b689b..38dfe2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,6 @@
2
- org.metricshub
ssh-java
SSH Java Client
1.0.03-SNAPSHOT
@@ -56,8 +55,8 @@
- Kawtar Bakour (@KawtarBK9)
- kawtar@sentrysoftware.com
+ Safae Ajib (@SafaeAJ)
+ safae@sentrysoftware.com
maintainer
@@ -69,6 +68,13 @@
maintainer
+
+ Mohamed Maalej (@MedMaalej)
+ maalej@sentrysoftware.com
+
+ maintainer
+
+
@@ -111,12 +117,42 @@
org.mockito
- mockito-inline
- 5.2.0
+ mockito-junit-jupiter
+ 5.16.1
test
+
+
+
+
+
+ com.hubspot.maven.plugins
+ prettier-maven-plugin
+ 0.21
+
+ 2.5.0
+ false
+ false
+
+ src/main/java/**/*.java
+ src/test/java/**/*.java
+
+
+
+
+ validate
+
+ check
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java
index ed12d21..b71501f 100644
--- a/src/main/java/org/metricshub/ssh/SshClient.java
+++ b/src/main/java/org/metricshub/ssh/SshClient.java
@@ -25,6 +25,15 @@
* ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
*/
+import com.trilead.ssh2.ChannelCondition;
+import com.trilead.ssh2.Connection;
+import com.trilead.ssh2.InteractiveCallback;
+import com.trilead.ssh2.SCPClient;
+import com.trilead.ssh2.SFTPv3Client;
+import com.trilead.ssh2.SFTPv3DirectoryEntry;
+import com.trilead.ssh2.SFTPv3FileAttributes;
+import com.trilead.ssh2.SFTPv3FileHandle;
+import com.trilead.ssh2.Session;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -39,17 +48,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.InteractiveCallback;
-import com.trilead.ssh2.SCPClient;
-import com.trilead.ssh2.SFTPv3Client;
-import com.trilead.ssh2.SFTPv3DirectoryEntry;
-import com.trilead.ssh2.SFTPv3FileAttributes;
-import com.trilead.ssh2.SFTPv3FileHandle;
-import com.trilead.ssh2.Session;
-
-
/**
* SSH Client that lets you perform basic SSH operations
*
@@ -173,7 +171,6 @@ public void close() {
}
}
-
/**
* Authenticate the SSH Client against the SSH server using a private key
*
@@ -187,9 +184,10 @@ public void close() {
@Deprecated
public boolean authenticate(String username, String privateKeyFile, String password) throws IOException {
return authenticate(
- username,
- privateKeyFile != null ? new File(privateKeyFile) : null,
- password != null ? password.toCharArray() : null);
+ username,
+ privateKeyFile != null ? new File(privateKeyFile) : null,
+ password != null ? password.toCharArray() : null
+ );
}
/**
@@ -202,12 +200,12 @@ public boolean authenticate(String username, String privateKeyFile, String passw
* @throws IOException
*/
public boolean authenticate(String username, File privateKeyFile, char[] password) throws IOException {
-
if (sshConnection.isAuthMethodAvailable(username, "publickey")) {
return sshConnection.authenticateWithPublicKey(
- username,
- privateKeyFile,
- password != null ? String.valueOf(password) : null);
+ username,
+ privateKeyFile,
+ password != null ? String.valueOf(password) : null
+ );
}
return false;
@@ -236,37 +234,45 @@ public boolean authenticate(String username, String password) throws IOException
* @throws IOException
*/
public boolean authenticate(String username, char[] password) throws IOException {
-
// Is the "password" method available? If yes, try it first
// Using normal login & password
- if (sshConnection.isAuthMethodAvailable(username, "password") &&
- sshConnection.authenticateWithPassword(username, password != null ? String.valueOf(password) : null)) {
+ if (
+ sshConnection.isAuthMethodAvailable(username, "password") &&
+ sshConnection.authenticateWithPassword(username, password != null ? String.valueOf(password) : null)
+ ) {
return true;
}
// Now, is the "keyboard-interactive" method available?
if (sshConnection.isAuthMethodAvailable(username, "keyboard-interactive")) {
- return sshConnection.authenticateWithKeyboardInteractive(username, new InteractiveCallback() {
-
- @Override
- public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws Exception {
-
- // Prepare responses to the challenges
- String[] challengeResponse = new String[numPrompts];
- for (int i = 0; i < numPrompts; i++) {
- // If we're told the input can be displayed (echoed),
- // we'll assume this is not a password
- // that we're being asked for, hence the username.
- // Otherwise, we'll send the password
- if (echo[i]) {
- challengeResponse[i] = username;
- } else {
- challengeResponse[i] = password != null ? String.valueOf(password) : null;
+ return sshConnection.authenticateWithKeyboardInteractive(
+ username,
+ new InteractiveCallback() {
+ @Override
+ public String[] replyToChallenge(
+ String name,
+ String instruction,
+ int numPrompts,
+ String[] prompt,
+ boolean[] echo
+ ) throws Exception {
+ // Prepare responses to the challenges
+ String[] challengeResponse = new String[numPrompts];
+ for (int i = 0; i < numPrompts; i++) {
+ // If we're told the input can be displayed (echoed),
+ // we'll assume this is not a password
+ // that we're being asked for, hence the username.
+ // Otherwise, we'll send the password
+ if (echo[i]) {
+ challengeResponse[i] = username;
+ } else {
+ challengeResponse[i] = password != null ? String.valueOf(password) : null;
+ }
}
+ return challengeResponse;
}
- return challengeResponse;
}
- });
+ );
}
// If none of the above methods are available, just quit
@@ -293,7 +299,6 @@ public boolean authenticate(String username) throws IOException {
* @throws IOException
*/
public String readFileAttributes(String filePath) throws IOException {
-
// Sanity check
checkIfAuthenticated();
@@ -318,13 +323,20 @@ public String readFileAttributes(String filePath) throws IOException {
// Build the result in the same format as the PSL function file()
StringBuilder pslFileResult = new StringBuilder();
pslFileResult
- .append(fileAttributes.mtime.toString()).append("\t")
- .append(fileAttributes.atime.toString()).append("\t-\t")
- .append(Integer.toString(fileAttributes.permissions & 0000777, 8)).append("\t")
- .append(fileAttributes.size.toString()).append("\t-\t")
- .append(fileType).append("\t")
- .append(fileAttributes.uid.toString()).append("\t")
- .append(fileAttributes.gid.toString()).append("\t")
+ .append(fileAttributes.mtime.toString())
+ .append("\t")
+ .append(fileAttributes.atime.toString())
+ .append("\t-\t")
+ .append(Integer.toString(fileAttributes.permissions & 0000777, 8))
+ .append("\t")
+ .append(fileAttributes.size.toString())
+ .append("\t-\t")
+ .append(fileType)
+ .append("\t")
+ .append(fileAttributes.uid.toString())
+ .append("\t")
+ .append(fileAttributes.gid.toString())
+ .append("\t")
.append(sftpClient.canonicalPath(filePath));
// Deallocate
@@ -334,24 +346,30 @@ public String readFileAttributes(String filePath) throws IOException {
return pslFileResult.toString();
}
- private StringBuilder listSubDirectory(SFTPv3Client sftpClient, String remoteDirectoryPath, Pattern fileMaskPattern, boolean includeSubfolders, Integer depth, StringBuilder resultBuilder)
- throws IOException {
-
+ private StringBuilder listSubDirectory(
+ SFTPv3Client sftpClient,
+ String remoteDirectoryPath,
+ Pattern fileMaskPattern,
+ boolean includeSubfolders,
+ Integer depth,
+ StringBuilder resultBuilder
+ ) throws IOException {
if (depth <= 15) {
@SuppressWarnings("unchecked")
Vector pathContents = sftpClient.ls(remoteDirectoryPath);
// Fix the remoteDirectoryPath (without the last '/')
- if (remoteDirectoryPath.endsWith("/"))
+ if (remoteDirectoryPath.endsWith("/")) {
remoteDirectoryPath = remoteDirectoryPath.substring(0, remoteDirectoryPath.lastIndexOf("/"));
+ }
depth++;
for (SFTPv3DirectoryEntry file : pathContents) {
-
String filename = file.filename.trim();
- if (filename.equals(".") || filename.equals(".."))
+ if (filename.equals(".") || filename.equals("..")) {
continue;
+ }
SFTPv3FileAttributes fileAttributes = file.attributes;
String filePath = remoteDirectoryPath + "/" + filename;
@@ -361,10 +379,15 @@ private StringBuilder listSubDirectory(SFTPv3Client sftpClient, String remoteDir
continue;
}
- if (((fileAttributes.permissions & 0100000) == 0100000) || ((fileAttributes.permissions & 0060000) == 0060000) || ((fileAttributes.permissions & 0020000) == 0020000)
- || ((fileAttributes.permissions & 0140000) == 0140000)) {
+ // CHECKSTYLE:OFF
+ if (
+ ((fileAttributes.permissions & 0100000) == 0100000) ||
+ ((fileAttributes.permissions & 0060000) == 0060000) ||
+ ((fileAttributes.permissions & 0020000) == 0020000) ||
+ ((fileAttributes.permissions & 0140000) == 0140000)
+ ) {
// Regular/Block/Character/Socket files
- Matcher m = fileMaskPattern.matcher(filename);
+ final Matcher m = fileMaskPattern.matcher(filename);
if (m.find()) {
resultBuilder
.append(filePath)
@@ -372,16 +395,18 @@ private StringBuilder listSubDirectory(SFTPv3Client sftpClient, String remoteDir
.append(fileAttributes.mtime.toString())
.append(";")
.append(fileAttributes.size.toString())
- .append("\n")
- ;
+ .append("\n");
}
continue;
}
+ // CHECKSTYLE:ON
if ((fileAttributes.permissions & 0040000) == 0040000) {
// Directory
- if (includeSubfolders)
- resultBuilder = listSubDirectory(sftpClient, filePath, fileMaskPattern, includeSubfolders, depth, resultBuilder);
+ if (includeSubfolders) {
+ resultBuilder =
+ listSubDirectory(sftpClient, filePath, fileMaskPattern, includeSubfolders, depth, resultBuilder);
+ }
}
}
}
@@ -401,8 +426,8 @@ private StringBuilder listSubDirectory(SFTPv3Client sftpClient, String remoteDir
* @throws IOException When something bad happens while communicating with the remote host
* @throws IllegalStateException If called while not yet connected
*/
- public String listFiles(String remoteDirectoryPath, String regExpFileMask, boolean includeSubfolders) throws IOException {
-
+ public String listFiles(String remoteDirectoryPath, String regExpFileMask, boolean includeSubfolders)
+ throws IOException {
checkIfAuthenticated();
// Create an SFTP Client
@@ -445,7 +470,6 @@ public String listFiles(String remoteDirectoryPath, String regExpFileMask, boole
* when the session hasn't been properly authenticated first
*/
public String readFile(String remoteFilePath, Long readOffset, Integer readSize) throws IOException {
-
checkIfAuthenticated();
// Create an SFTP Client
@@ -453,8 +477,9 @@ public String readFile(String remoteFilePath, Long readOffset, Integer readSize)
// Where do we read from (offset)?
long offset = 0; // from the beginning by default
- if (readOffset != null)
+ if (readOffset != null) {
offset = readOffset; // use the set offset
+ }
// Open the remote file
SFTPv3FileHandle handle = sftpClient.openFileRO(remoteFilePath);
@@ -483,7 +508,6 @@ public String readFile(String remoteFilePath, Long readOffset, Integer readSize)
// Loop until there is nothing else to read
while (remainingBytes > 0) {
-
// Read by chunk of 8192 bytes. However, if there is less to read,
// well, read less.
if (remainingBytes < READ_BUFFER_SIZE) {
@@ -528,7 +552,6 @@ public String readFile(String remoteFilePath, Long readOffset, Integer readSize)
* @throws IllegalStateException when not connected and authenticated yet
*/
public void removeFile(String[] remoteFilePathArray) throws IOException {
-
checkIfAuthenticated();
// Create an SFTP Client
@@ -540,7 +563,6 @@ public void removeFile(String[] remoteFilePathArray) throws IOException {
for (String remoteFilePath : remoteFilePathArray) {
sftpClient.rm(remoteFilePath);
}
-
} catch (IOException e) {
// Okay, we got an issue here with the SFTP client
// We're going to try again but with a good old "rm" command...
@@ -560,13 +582,11 @@ public void removeFile(String[] remoteFilePathArray) throws IOException {
}
}
} finally {
-
// Close the SFTP client
if (sftpClient != null) {
sftpClient.close();
}
}
-
}
/**
@@ -587,6 +607,7 @@ public void removeFile(String remoteFilePath) throws IOException {
*
*/
public static class CommandResult {
+
/**
* Whether the command was successful or not
*/
@@ -608,7 +629,6 @@ public static class CommandResult {
* The result of the command (stdout and stderr is merged into result)
*/
public String result = "";
-
}
/**
@@ -633,7 +653,6 @@ public CommandResult executeCommand(String command) throws IOException {
* @throws IOException when there is a problem while communicating with the remote system
*/
public CommandResult executeCommand(String command, int timeout) throws IOException {
-
openSession();
InputStream stdout = sshSession.getStdout();
@@ -645,8 +664,7 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
CommandResult commandResult = new CommandResult();
// Output to a byte stream
- try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
-
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
// Time to be remembered
long startTime = System.currentTimeMillis();
long timeoutTime;
@@ -662,10 +680,12 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
int waitForCondition = 0;
long currentTime;
- while (!hasSessionClosed(waitForCondition) &&
- !hasEndOfFileSession(waitForCondition) &&
- ((currentTime = System.currentTimeMillis()) < timeoutTime)) {
-
+ // CHECKSTYLE:OFF
+ while (
+ !hasSessionClosed(waitForCondition) &&
+ !hasEndOfFileSession(waitForCondition) &&
+ ((currentTime = System.currentTimeMillis()) < timeoutTime)
+ ) {
// Wait for new data (timeout = 5 seconds)
waitForCondition = waitForNewData(Math.min(timeoutTime - currentTime, 5000));
@@ -677,21 +697,18 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
if (hasStderrData(waitForCondition)) {
transferAllBytes(stderr, output);
}
-
}
+ // CHECKSTYLE:ON
// What time is it?
currentTime = System.currentTimeMillis();
if (currentTime >= timeoutTime) {
-
// If we exceeded the timeout, we're not successful
// Build the "timed out" result
commandResult.success = false;
commandResult.result = "Timeout (" + timeout / 1000 + " seconds)";
-
} else {
-
// We completed in time
// Execution time (in seconds)
@@ -705,7 +722,6 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
// Stringify the stdout stream
commandResult.result = new String(output.toByteArray(), charset);
-
}
}
@@ -715,7 +731,7 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
/**
* Starts an interactive session.
- *
+ *
* @param in Where the input is coming from (typically System.in)
* @param out Where the output has to go (e.g. System.out)
* @throws IllegalStateException when not connected and authenticated
@@ -723,7 +739,6 @@ public CommandResult executeCommand(String command, int timeout) throws IOExcept
* @throws InterruptedException when a thread is interrupted
*/
public void interactiveSession(InputStream in, OutputStream out) throws IOException, InterruptedException {
-
openSession();
openTerminal();
@@ -757,7 +772,6 @@ public void run() {
int waitForCondition = 0;
while (!hasSessionClosed(waitForCondition) && !hasEndOfFileSession(waitForCondition)) {
-
// Wait for new data (timeout = 5 seconds)
waitForCondition = waitForNewData(5000L);
@@ -769,7 +783,6 @@ public void run() {
if (hasStderrData(waitForCondition)) {
transferAllBytes(stderr, out);
}
-
}
// Attempt to interrupt the stdinPipeThread thread
@@ -788,8 +801,8 @@ public void run() {
* @param fileMode
* @throws IOException
*/
- public void scp(String localFilePath, String remoteFilename, String remoteDirectory, String fileMode) throws IOException {
-
+ public void scp(String localFilePath, String remoteFilename, String remoteDirectory, String fileMode)
+ throws IOException {
checkIfAuthenticated();
// Create the SCP client
@@ -797,7 +810,6 @@ public void scp(String localFilePath, String remoteFilename, String remoteDirect
// Copy the file
scpClient.put(localFilePath, remoteFilename, remoteDirectory, fileMode);
-
}
/**
@@ -806,7 +818,6 @@ public void scp(String localFilePath, String remoteFilename, String remoteDirect
* @throws IOException When an I/O error occurred.
*/
public void openSession() throws IOException {
-
checkIfConnected();
checkIfAuthenticated();
@@ -820,13 +831,12 @@ public void openSession() throws IOException {
*
* @throws IOException When an I/O error occurred.
*/
- public void openTerminal() throws IOException {
-
+ public void openTerminal() throws IOException {
checkIfConnected();
checkIfAuthenticated();
checkIfSessionOpened();
- getSshSession().requestPTY("dumb", 10000, 24, 640, 480, new byte[] {53, 0, 0, 0, 0, 0});
+ getSshSession().requestPTY("dumb", 10000, 24, 640, 480, new byte[] { 53, 0, 0, 0, 0, 0 });
getSshSession().startShell();
}
@@ -837,7 +847,6 @@ public void openTerminal() throws IOException {
* @throws IOException When an I/O error occurred.
*/
public void write(final String text) throws IOException {
-
if (text == null || text.isEmpty()) {
return;
}
@@ -880,7 +889,6 @@ public void write(final String text) throws IOException {
* @throws IOException When an I/O error occurred.
*/
public Optional read(final int size, final int timeout) throws IOException {
-
Utils.checkArgumentNotZeroOrNegative(timeout, "timeout");
checkIfConnected();
@@ -894,8 +902,7 @@ public Optional read(final int size, final int timeout) throws IOExcepti
Utils.checkNonNullField(stdout, "stdout");
Utils.checkNonNullField(stderr, "stderr");
- try (final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
-
+ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
// Wait for new data
final int waitForCondition = waitForNewData(timeout * 1000L);
@@ -916,9 +923,9 @@ public Optional read(final int size, final int timeout) throws IOExcepti
transferBytes(stderr, byteArrayOutputStream, size - stdoutRead);
}
- return stdoutData || stderrData ?
- Optional.of(new String(byteArrayOutputStream.toByteArray(), charset)) :
- Optional.empty();
+ return stdoutData || stderrData
+ ? Optional.of(new String(byteArrayOutputStream.toByteArray(), charset))
+ : Optional.empty();
}
}
@@ -984,11 +991,9 @@ static boolean hasStderrData(final int waitForCondition) {
*/
int waitForNewData(final long timeout) {
return sshSession.waitForCondition(
- ChannelCondition.STDOUT_DATA |
- ChannelCondition.STDERR_DATA |
- ChannelCondition.EOF |
- ChannelCondition.CLOSED,
- timeout);
+ ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF | ChannelCondition.CLOSED,
+ timeout
+ );
}
/**
@@ -1026,10 +1031,7 @@ public void checkIfSessionOpened() {
* @return The total number of copy bytes.
* @throws IOException When an I/O error occurred.
*/
- static int transferAllBytes(
- final InputStream inputStream,
- final OutputStream outputStream) throws IOException {
-
+ static int transferAllBytes(final InputStream inputStream, final OutputStream outputStream) throws IOException {
return transferBytes(inputStream, outputStream, -1);
}
@@ -1042,19 +1044,15 @@ static int transferAllBytes(
* @return The total number of copy bytes.
* @throws IOException When an I/O error occurred.
*/
- static int transferBytes(
- final InputStream inputStream,
- final OutputStream outputStream,
- final int size) throws IOException {
-
+ static int transferBytes(final InputStream inputStream, final OutputStream outputStream, final int size)
+ throws IOException {
final int bufferSize = size > 0 && size < READ_BUFFER_SIZE ? size : READ_BUFFER_SIZE;
final byte[] buffer = new byte[bufferSize];
int total = 0;
int bytesRead = 0;
- while(inputStream.available() > 0 && (bytesRead = inputStream.read(buffer)) > 0) {
-
+ while (inputStream.available() > 0 && (bytesRead = inputStream.read(buffer)) > 0) {
final int bytesCopy = Math.min(bytesRead, READ_BUFFER_SIZE);
outputStream.write(Arrays.copyOf(buffer, bytesCopy));
diff --git a/src/main/java/org/metricshub/ssh/Utils.java b/src/main/java/org/metricshub/ssh/Utils.java
index b22c6ac..56198ac 100644
--- a/src/main/java/org/metricshub/ssh/Utils.java
+++ b/src/main/java/org/metricshub/ssh/Utils.java
@@ -25,7 +25,6 @@
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
-
/**
* Utility Class (static), to be used anywhere in Matsya, including in
* standalone JARs (in CLI mode)
@@ -34,17 +33,16 @@
*/
public class Utils {
- private Utils() { }
+ private Utils() {}
/**
* Returns the proper Charset that can decode/encode the specified locale
- *
+ *
* @param locale The locale we're dealing with, as formatted in the LANG environment variable (e.g. zh_CN.utf8)
* @param defaultCharset The default Charset to use if the specified locale doesn't match any supported Charset
* @return The appropriate Charset instance
*/
public static Charset getCharsetFromLocale(final String locale, final Charset defaultCharset) {
-
// What charset will we be dealing with coming from and going to the PATROL Agent
Charset charset = defaultCharset;
if (locale != null && !locale.isEmpty()) {
@@ -57,7 +55,7 @@ public static Charset getCharsetFromLocale(final String locale, final Charset de
if ("gb".equalsIgnoreCase(charsetName)) {
charsetName = "GBK";
}
- try {
+ try {
charset = Charset.forName(charsetName);
} catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
/* Unfortunately, there is nothing we can do here, as debug is not even set yet */
@@ -65,7 +63,7 @@ public static Charset getCharsetFromLocale(final String locale, final Charset de
}
}
}
- }
+ }
return charset;
}
@@ -73,7 +71,7 @@ public static Charset getCharsetFromLocale(final String locale, final Charset de
/**
* Returns the proper Charset that can decode/encode the specified locale, or UTF-8 if specified locale
* cannot be converted to a Charset.
- *
+ *
* @param locale The locale we're dealing with, as formatted in the LANG environment variable (e.g. zh_CN.utf8)
* @return The appropriate Charset instance
*/
@@ -94,7 +92,6 @@ public static void checkNonNullField(final T field, final String name) {
}
}
-
/**
* Check if the required argument is not negative or zero.
*
@@ -107,6 +104,4 @@ public static void checkArgumentNotZeroOrNegative(final long argument, final Str
throw new IllegalArgumentException(String.format("%s=%d must not be negative or zero.", name, argument));
}
}
-
-
}
diff --git a/src/site/resources/css/site.css b/src/site/resources/css/site.css
new file mode 100644
index 0000000..b7690bc
--- /dev/null
+++ b/src/site/resources/css/site.css
@@ -0,0 +1,86 @@
+/*-
+ * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
+ * Open MetricsHub Web Site
+ * ჻჻჻჻჻჻
+ * Copyright (C) 2025 MetricsHub
+ * ჻჻჻჻჻჻
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
+ */
+
+/* Poppins font */
+@font-face {
+ font-family: 'Poppins';
+ src: url('../fonts/Poppins-ExtraBold.woff2') format('woff2'),
+ url('../fonts/Poppins-ExtraBold.woff') format('woff'),
+ url('../fonts/Poppins-ExtraBold.ttf') format('truetype'),
+ local('Helvetica');
+ font-weight: 800;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('../fonts/Poppins-Bold.woff2') format('woff2'),
+ url('../fonts/Poppins-Bold.woff') format('woff'),
+ url('../fonts/Poppins-Bold.ttf') format('truetype'),
+ local('Helvetica');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('../fonts/Poppins-Regular.woff2') format('woff2'),
+ url('../fonts/Poppins-Regular.woff') format('woff'),
+ url('../fonts/Poppins-Regular.ttf') format('truetype'),
+ local('Helvetica');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* MetricsHub fonts */
+:root {
+ --title-font: "Poppins", sans-serif;
+ --heading-font: "Poppins", sans-serif;
+ --content-font: "Poppins", sans-serif;
+
+ --content-font-size: medium;
+ --banner-font-size: 40px;
+ --banner-font-weight: 800;
+
+}
+
+ /* MetricsHub colors */
+body {
+ --main-fgcolor: #212529;
+ --banner-bgcolor: #266fd0;
+ --link-color: #266fd0;
+ --alternate-bgcolor: #266fd0;
+ --alternate-fgcolor: #fff;
+}
+
+body.dark {
+ --link-color: #7cb6ff;
+ --main-bgcolor: #262626;
+ --main-fgcolor: #e9ecef;
+}
\ No newline at end of file
diff --git a/src/site/resources/favicon.ico b/src/site/resources/favicon.ico
new file mode 100644
index 0000000..61a2a88
Binary files /dev/null and b/src/site/resources/favicon.ico differ
diff --git a/src/site/resources/fonts/Poppins-Bold.ttf b/src/site/resources/fonts/Poppins-Bold.ttf
new file mode 100644
index 0000000..00559ee
Binary files /dev/null and b/src/site/resources/fonts/Poppins-Bold.ttf differ
diff --git a/src/site/resources/fonts/Poppins-Bold.woff b/src/site/resources/fonts/Poppins-Bold.woff
new file mode 100644
index 0000000..c979105
Binary files /dev/null and b/src/site/resources/fonts/Poppins-Bold.woff differ
diff --git a/src/site/resources/fonts/Poppins-Bold.woff2 b/src/site/resources/fonts/Poppins-Bold.woff2
new file mode 100644
index 0000000..13e0e28
Binary files /dev/null and b/src/site/resources/fonts/Poppins-Bold.woff2 differ
diff --git a/src/site/resources/fonts/Poppins-ExtraBold.ttf b/src/site/resources/fonts/Poppins-ExtraBold.ttf
new file mode 100644
index 0000000..df70936
Binary files /dev/null and b/src/site/resources/fonts/Poppins-ExtraBold.ttf differ
diff --git a/src/site/resources/fonts/Poppins-ExtraBold.woff b/src/site/resources/fonts/Poppins-ExtraBold.woff
new file mode 100644
index 0000000..0d7982a
Binary files /dev/null and b/src/site/resources/fonts/Poppins-ExtraBold.woff differ
diff --git a/src/site/resources/fonts/Poppins-ExtraBold.woff2 b/src/site/resources/fonts/Poppins-ExtraBold.woff2
new file mode 100644
index 0000000..ad86d02
Binary files /dev/null and b/src/site/resources/fonts/Poppins-ExtraBold.woff2 differ
diff --git a/src/site/resources/fonts/Poppins-Regular.ttf b/src/site/resources/fonts/Poppins-Regular.ttf
new file mode 100644
index 0000000..9f0c71b
Binary files /dev/null and b/src/site/resources/fonts/Poppins-Regular.ttf differ
diff --git a/src/site/resources/fonts/Poppins-Regular.woff2 b/src/site/resources/fonts/Poppins-Regular.woff2
new file mode 100644
index 0000000..964d6d2
Binary files /dev/null and b/src/site/resources/fonts/Poppins-Regular.woff2 differ
diff --git a/src/site/site.xml b/src/site/site.xml
index 6ab08a6..3f61bd5 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -4,7 +4,7 @@
org.sentrysoftware.maven
sentry-maven-skin
- 6.2.00
+ 6.4.01
@@ -14,13 +14,14 @@
images/metricshub-logo-only.png
- https://metricsHub.com
+ https://metricsHub.org
+