detectShellEnv() throws SSHException {
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8192);
+ final ChannelShell shell = openChannel(session, "shell");
+ try {
+ // Command is executable both in Linux and Windows os
+ shell.setInputStream(listInputStream(List.of("env || set"), UTF_8));
+ shell.setPty(false);
+ InputStream in = shell.getInputStream();
+ PumpThread t1 = new PumpThread(in, outputStream);
+ t1.start();
+ shell.connect();
+ t1.join();
+ // Don't check the exit code, returns -1 on windows.
+ // Expect UTF-8 for now. It will be probably different on Windows,
+ // but we will parse it from the output.
+ String output = outputStream.toString(UTF_8);
+ LOG.log(DEBUG, () -> "Environment options - command output: \n" + output);
+ return parseProperties(output);
+ } catch (Exception e) {
+ throw new SSHException("Could not detect shell environment options. Output: "
+ + outputStream.toString(UTF_8), e);
+ } finally {
+ shell.disconnect();
+ }
+ }
+
+
+ /**
+ * Unpacks the zip file to the target directory.
+ *
+ * On Linux it uses cd
and jar
commands.
+ * On Windows it uses PowerShell commands.
+ *
+ * @param remoteZipFile
+ * @param remoteDir
+ * @throws SSHException
+ */
+ public void unzip(Path remoteZipFile, Path remoteDir) throws SSHException {
+ final String unzipCommand;
+ if (capabilities.getOperatingSystem() == OperatingSystem.WINDOWS) {
+ unzipCommand = "PowerShell.exe -Command \"Expand-Archive -LiteralPath '" + remoteZipFile
+ + "' -DestinationPath '" + remoteDir + "'\"";
+ } else {
+ unzipCommand = "cd \"" + remoteDir + "\"; jar -xvf \"" + remoteZipFile + "\"";
+ }
+
+ final StringBuilder output = new StringBuilder();
+ final int status = exec(unzipCommand, null, output);
+ if (status != 0) {
+ throw new SSHException("Failed unpacking glassfish zip file. Output: " + output + ".");
+ }
+ LOG.log(DEBUG, () -> "Unpacked " + remoteZipFile + " to directory " + remoteDir);
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command line parts
+ * @param stdinLines - lines used to fake standard input in STDIN stream. Can be null.
+ * @param output - empty collector of the output. Can be null.
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(List command, List stdinLines, StringBuilder output) throws SSHException {
+ return exec(commandListToQuotedString(command), listInputStream(stdinLines, capabilities.getCharset()), output);
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command line parts
+ * @param stdinLines - lines used to fake standard input in STDIN stream. Can be null.
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(List command, List stdinLines) throws SSHException {
+ return exec(commandListToQuotedString(command), stdinLines);
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command to execute. If it has arguments, better use {@link #exec(List, List)}.
+ * @param stdinLines - lines used to fake standard input in STDIN stream. Can be null.
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(String command, List stdinLines) throws SSHException {
+ return exec(command, listInputStream(stdinLines, capabilities.getCharset()));
+ }
+
+
+ /**
+ * SFTP exec command without STDIN and without reading the output.
+ * Executes a command on the remote system via ssh.
+ *
+ * @param command - command to execute. If it has arguments, better use {@link #exec(List, List)}.
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(final String command) throws SSHException {
+ return exec(command, (InputStream) null);
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command to execute. If it has arguments, better use {@link #exec(List, List)}.
+ * @param stdin - stream used to fake standard input in STDIN stream. Can be null.
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(final String command, final InputStream stdin) throws SSHException {
+ return exec(command, stdin, null);
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command to execute. If it has arguments, better use {@link #exec(List, List, StringBuilder)}.
+ * @param stdin - stream used to fake standard input in STDIN stream. Can be null.
+ * @param output
+ * @return exit code
+ * @throws SSHException
+ */
+ public int exec(final String command, final InputStream stdin, final StringBuilder output) throws SSHException {
+ return exec(command, stdin, output, capabilities.getCharset());
+ }
+
+
+ /**
+ * SFTP exec command.
+ * Executes a command on the remote system via ssh, optionally sending
+ * lines of data to the remote process's System.in.
+ *
+ * @param command - command to execute. If it has arguments, better use {@link #exec(List, List, StringBuilder)}.
+ * @param stdin - stream used to fake standard input in STDIN stream. Can be null.
+ * @param output
+ * @return exit code
+ * @throws SSHException
+ */
+ int exec(final String command, final InputStream stdin, final StringBuilder output, final Charset charset)
+ throws SSHException {
+ LOG.log(INFO, () -> "Executing command " + command + " on host: " + session.getHost());
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8192);
+ final ChannelExec execChannel = openChannel(session, "exec");
+ try {
+ execChannel.setInputStream(stdin);
+ execChannel.setCommand(command);
+ InputStream in = execChannel.getInputStream();
+ PumpThread t1 = new PumpThread(in, outputStream);
+ t1.start();
+ PumpThread t2 = new PumpThread(execChannel.getErrStream(), outputStream);
+ t2.start();
+ execChannel.connect();
+
+ t1.join();
+ t2.join();
+ if (output != null || LOG.isLoggable(DEBUG)) {
+ String commandOutput = outputStream.toString(charset);
+ LOG.log(DEBUG, () -> "Command output: \n" + commandOutput);
+ if (output != null) {
+ output.append(commandOutput);
+ }
+ }
+ if (execChannel.isClosed()) {
+ return execChannel.getExitStatus();
+ }
+ return -1;
+ } catch (Exception e) {
+ throw new SSHException("Command " + command + " failed. Output: " + outputStream.toString(charset), e);
+ } finally {
+ execChannel.disconnect();
+ }
+ }
+
+
+ /**
+ * @return new {@link SFTPClient}
+ * @throws SSHException if the connection failed, ie because the server does not support SFTP.
+ */
+ public SFTPClient createSFTPClient() throws SSHException {
+ return new SFTPClient((ChannelSftp) openChannel(session, "sftp"));
+ }
+
+
+ @Override
+ public void close() {
+ if (session.isConnected()) {
+ session.disconnect();
+ }
+ }
+
+
+ /**
+ * Take a command in the form of a list and convert it to a command string.
+ * If any string in the list has spaces then the string is quoted before
+ * being added to the final command string.
+ *
+ * @param command
+ * @return
+ */
+ private static String commandListToQuotedString(List command) {
+ if (command.size() == 1) {
+ return command.get(0);
+ }
+ StringBuilder commandBuilder = new StringBuilder();
+ boolean first = true;
+
+ for (String s : command) {
+ if (!first) {
+ commandBuilder.append(" ");
+ } else {
+ first = false;
+ }
+ if (s.contains(" ")) {
+ // Quote parts of the command that contain a space
+ commandBuilder.append(FileUtils.quoteString(s));
+ } else {
+ commandBuilder.append(s);
+ }
+ }
+ return commandBuilder.toString();
+ }
+
+
+ private static InputStream listInputStream(final List stdinLines, Charset charset) {
+ if (stdinLines == null) {
+ return null;
+ }
+ try {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (String line : stdinLines) {
+ baos.write(line.getBytes(charset));
+ baos.write('\n');
+ }
+ return new ByteArrayInputStream(baos.toByteArray());
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot copy the input to UTF-8 byte array input stream.", e);
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private static T openChannel(Session session, String type) throws SSHException {
+ try {
+ return (T) session.openChannel(type);
+ } catch (JSchException e) {
+ throw new SSHException("Could not open the session of type=" + type, e);
+ }
+ }
+
+
+ private static Map parseProperties(String output) {
+ String[] lines = output.split("\\R");
+ Map properties = new TreeMap<>();
+ for (String line : lines) {
+ int equalSignPosition = line.indexOf('=');
+ if (equalSignPosition <= 0) {
+ continue;
+ }
+ String key = line.substring(0, equalSignPosition);
+ String value = equalSignPosition == line.length() - 1 ? "" : line.substring(equalSignPosition + 1);
+ properties.put(key.strip(), value.strip());
+ }
+ return properties;
+ }
+
+
+ /**
+ * Pumps {@link InputStream} to {@link OutputStream}.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+ private static final class PumpThread extends Thread {
+ private final InputStream in;
+ private final OutputStream out;
+
+ public PumpThread(InputStream in, OutputStream out) {
+ super("pump thread");
+ this.in = in;
+ this.out = out;
+ }
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[8192];
+ try {
+ while(true) {
+ int len = in.read(buf);
+ if(len<0) {
+ in.close();
+ return;
+ }
+ out.write(buf,0,len);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/sftp/SFTPClient.java b/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/sftp/SFTPClient.java
index 327e5a43933..d56decc950b 100644
--- a/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/sftp/SFTPClient.java
+++ b/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/sftp/SFTPClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -18,28 +18,57 @@
package org.glassfish.cluster.ssh.sftp;
import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
-import org.glassfish.cluster.ssh.util.SSHUtil;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
-public class SFTPClient implements AutoCloseable {
+import org.glassfish.cluster.ssh.launcher.RemoteSystemCapabilities;
+import org.glassfish.cluster.ssh.launcher.SSHException;
+import org.glassfish.cluster.ssh.launcher.SSHSession;
- private Session session = null;
+import static java.lang.System.Logger.Level.TRACE;
- private ChannelSftp sftpChannel = null;
+/**
+ * SFTP client.
+ *
+ * @see SSHSession
+ */
+public class SFTPClient implements AutoCloseable {
+ private static final Logger LOG = System.getLogger(SFTPClient.class.getName());
+ /**
+ * This is required on Linux based hosts; directories are not in the list on Windows.
+ */
+ private static final Predicate PREDICATE_NO_DOTS = p -> !".".equals(p.getFilename())
+ && !"..".equals(p.getFilename());
- public SFTPClient(Session session) throws JSchException {
- this.session = session;
- sftpChannel = (ChannelSftp) session.openChannel("sftp");
- sftpChannel.connect();
- SSHUtil.register(session);
- }
+ private final ChannelSftp sftpChannel;
- public ChannelSftp getSftpChannel() {
- return sftpChannel;
+ /**
+ * Creates the instance which immediately tries to open the SFTP connection..
+ *
+ * @param channel
+ * @throws SSHException if the connection could not be established, usually because the SSH
+ * server doesn't support SFTP.
+ */
+ public SFTPClient(ChannelSftp channel) throws SSHException {
+ this.sftpChannel = channel;
+ try {
+ this.sftpChannel.connect();
+ } catch (JSchException e) {
+ throw new SSHException("Failed to connect to the SFTP server. Is it correctly configured on the server?", e);
+ }
}
/**
@@ -48,69 +77,312 @@ public ChannelSftp getSftpChannel() {
*/
@Override
public void close() {
- if (session != null) {
- SSHUtil.unregister(session);
- session = null;
+ if (sftpChannel != null) {
+ sftpChannel.disconnect();
}
}
+
/**
- * Checks if the given path exists.
+ * @return Configured SSH server home directory. Usually user's home directory.
+ * @throws SSHException Command failed.
*/
- public boolean exists(String path) throws SftpException {
- return _stat(normalizePath(path))!=null;
+ public Path getHome() throws SSHException {
+ try {
+ return Path.of(sftpChannel.getHome());
+ } catch (SftpException e) {
+ throw new SSHException("Could not resolve SFTP Home path.", e);
+ }
}
+
/**
- * Graceful stat that returns null if the path doesn't exist.
+ * Makes sure that the directory exists, by creating it if necessary.
+ * @param path the remote path
+ * @throws SSHException Command failed.
*/
- public SftpATTRS _stat(String path) throws SftpException {
+ public void mkdirs(Path path) throws SSHException {
+ if (existsDirectory(path)) {
+ return;
+ }
+ Path current = Path.of("/");
+ for (Path part : path.normalize()) {
+ current = current.resolve(part);
+ if (existsDirectory(current)) {
+ continue;
+ }
+ try {
+ sftpChannel.mkdir(current.toString());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to create the directory " + path + '.', e);
+ }
+ }
+ }
+
+
+ /**
+ * @param path
+ * @return true if the path exists and is a directory
+ * @throws SSHException Command failed.
+ */
+ public boolean existsDirectory(Path path) throws SSHException {
+ SftpATTRS attrs = stat(path);
+ return attrs != null && attrs.isDir();
+ }
+
+
+ /**
+ * @param path
+ * @return true if the path exists, is a directory and is empty.
+ * @throws SSHException Command failed.
+ */
+ public boolean isEmptyDirectory(Path path) throws SSHException {
+ SftpATTRS attrs = stat(path);
+ return attrs != null && attrs.isDir() && ls(path, e -> true).isEmpty();
+ }
+
+
+ /**
+ * Recursively deletes the specified directory.
+ *
+ * @param path
+ * @param onlyContent
+ * @param exclude
+ * @throws SSHException Command failed. Usually some file is not removable or is open.
+ */
+ public void rmDir(Path path, boolean onlyContent, Path... exclude) throws SSHException {
+ if (!exists(path)) {
+ return;
+ }
+ // We use recursion while the channel is stateful
+ cd(path.getParent());
+ List content = lsDetails(path, p -> true);
+ for (LsEntry entry : content) {
+ final String filename = entry.getFilename();
+ final Path entryPath = path.resolve(filename);
+ if (isExcludedFromDeletion(filename, exclude)) {
+ LOG.log(TRACE, "Skipping excluded {0}", entryPath);
+ continue;
+ }
+ if (entry.getAttrs().isDir()) {
+ rmDir(entryPath, false, getSubDirectoryExclusions(filename, exclude));
+ } else {
+ LOG.log(TRACE, "Deleting file {0}", entryPath);
+ rm(entryPath);
+ }
+ }
+ if (!onlyContent) {
+ try {
+ sftpChannel.cd(path.getParent().toString());
+ LOG.log(TRACE, "Deleting directory {0}", path);
+ sftpChannel.rmdir(path.toString());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to delete directory: " + path + '.', e);
+ }
+ }
+ }
+
+
+ private static boolean isExcludedFromDeletion(String firstName, Path... exclusions) {
+ if (exclusions == null) {
+ return false;
+ }
+ return Arrays.stream(exclusions).filter(p -> p.getNameCount() == 1)
+ .anyMatch(p -> p.getFileName().toString().equals(firstName));
+ }
+
+
+ private static Path[] getSubDirectoryExclusions(String firstName, Path... exclusions) {
+ if (exclusions == null) {
+ return new Path[0];
+ }
+ return Arrays.stream(exclusions).filter(p -> p.getNameCount() > 1).filter(p -> p.startsWith(firstName))
+ .map(p -> p.subpath(1, p.getNameCount())).toArray(Path[]::new);
+ }
+
+
+ /**
+ * Upload local file to the remote file.
+ *
+ * @param localFile
+ * @param remoteFile
+ * @throws SSHException Command failed.
+ */
+ public void put(File localFile, Path remoteFile) throws SSHException {
try {
- return sftpChannel.stat(normalizePath(path));
+ sftpChannel.cd(remoteFile.getParent().toString());
+ sftpChannel.put(localFile.getAbsolutePath(), remoteFile.toString());
} catch (SftpException e) {
- int c = e.id;
- if (c == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ throw new SSHException(
+ "Failed to upload the local file " + localFile + " to remote file " + remoteFile + '.', e);
+ }
+ }
+
+
+ /**
+ * Downloads the remote file to the local file. The local file must not exist yet.
+ *
+ * @param remoteFile
+ * @param localFile
+ * @throws SSHException Command failed.
+ */
+ public void download(Path remoteFile, Path localFile) throws SSHException {
+ try (InputStream inputStream = sftpChannel.get(remoteFile.toString())) {
+ Files.copy(inputStream, localFile);
+ } catch (SftpException | IOException e) {
+ throw new SSHException(
+ "Failed to download the remote file " + remoteFile + " to local file " + localFile + '.', e);
+ }
+ }
+
+
+ /**
+ * Deletes the specified remote file.
+ *
+ * @param path
+ * @throws SSHException
+ */
+ public void rm(Path path) throws SSHException {
+ try {
+ sftpChannel.cd(path.getParent().toString());
+ sftpChannel.rm(path.toString());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to remove path " + path + '.', e);
+ }
+ }
+
+
+ /**
+ * @param path
+ * @return true if the remote path exists.
+ * @throws SSHException Command failed.
+ */
+ public boolean exists(Path path) throws SSHException {
+ return stat(path) != null;
+ }
+
+
+ /**
+ * Providing file details. This method follows symlinks.
+ *
+ * @param path
+ * @return {@link SftpATTRS} or null if the path doesn't exist.
+ * @throws SSHException Command failed.
+ */
+ public SftpATTRS stat(Path path) throws SSHException {
+ try {
+ return sftpChannel.stat(path.toString());
+ } catch (SftpException e) {
+ if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
return null;
}
- throw e;
+ throw new SSHException("Failed to call SFTP stat for " + path + '.', e);
}
}
+
/**
- * Makes sure that the directory exists, by creating it if necessary.
+ * Providing file details. This method does not follow symlinks.
+ *
+ * @param path
+ * @return {@link SftpATTRS} or null if the path doesn't exist.
+ * @throws SSHException Command failed.
*/
- public void mkdirs(String path, int posixPermission) throws SftpException {
- // remove trailing slash if present
- if (path.endsWith("/")) {
- path = path.substring(0, path.length() - 1);
+ public SftpATTRS lstat(Path path) throws SSHException {
+ try {
+ return sftpChannel.lstat(path.toString());
+ } catch (SftpException e) {
+ if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+ return null;
+ }
+ throw new SSHException("Failed to call SFTP lstat for " + path + '.', e);
}
+ }
- path = normalizePath(path);
- SftpATTRS attrs = _stat(path);
- if (attrs != null && attrs.isDir()) {
- return;
+
+ /**
+ * Calls SFTP MTIME for given path and millis.
+ *
+ * @param path
+ * @param millisSinceUnixEpoch
+ * @throws SSHException Command failed.
+ */
+ public void setTimeModified(Path path, long millisSinceUnixEpoch) throws SSHException {
+ try {
+ sftpChannel.setMtime(path.toString(), (int) (millisSinceUnixEpoch / 1000));
+ } catch (SftpException e) {
+ throw new SSHException("Failed to set time modification for path " + path + '.', e);
}
+ }
+
- int idx = path.lastIndexOf("/");
- if (idx>0) {
- mkdirs(path.substring(0,idx), posixPermission);
+ /**
+ * Calls SFTP CHMOD. Note that this command is not supported on Windows.
+ *
+ * @param path
+ * @param permissions
+ * @throws SSHException Command failed.
+ * @see RemoteSystemCapabilities#isChmodSupported()
+ */
+ public void chmod(Path path, int permissions) throws SSHException {
+ try {
+ sftpChannel.chmod(permissions, path.toString());
+ } catch (SftpException e) {
+ throw new SSHException(
+ "Failed to call chmod for remote path " + path + " and permissions " + permissions + ".", e);
}
- sftpChannel.mkdir(path);
- sftpChannel.chmod(posixPermission, path);
}
- public void chmod(String path, int permissions) throws SftpException {
- path = normalizePath(path);
- sftpChannel.chmod(permissions, path);
+
+ /**
+ * Changes the current directory on the remote SFTP server.
+ *
+ * @param path
+ * @throws SSHException Command failed.
+ */
+ public void cd(Path path) throws SSHException {
+ try {
+ sftpChannel.cd(path.toString());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to change the remote directory to " + path + '.', e);
+ }
}
- // Commands run in a shell on Windows need to have forward slashes.
- public static String normalizePath(String path){
- return path.replaceAll("\\\\","/");
+
+ /**
+ * Lists file names the given remote directory. Excludes current directory and the parent
+ * directory links (dot, double dot)
+ *
+ * @param path
+ * @param filter additional filter, ie. to filter by file extension.
+ * @return list of file names in the given directory
+ * @throws SSHException Command failed.
+ */
+ public List ls(Path path, Predicate filter) throws SSHException {
+ try {
+ return sftpChannel.ls(path.toString()).stream().filter(filter.and(PREDICATE_NO_DOTS))
+ .map(LsEntry::getFilename).collect(Collectors.toList());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to list remote directory " + path + '.', e);
+ }
}
- public void cd(String path) throws SftpException {
- path = normalizePath(path);
- sftpChannel.cd(path);
+
+ /**
+ * Lists entries in the given remote directory. Excludes current directory and the parent
+ * directory links (dot, double dot)
+ *
+ * @param path
+ * @param filter additional filter, ie. to filter by file extension.
+ * @return list of file names in the given directory
+ * @throws SSHException Command failed.
+ */
+ public List lsDetails(Path path, Predicate filter) throws SSHException {
+ try {
+ return sftpChannel.ls(path.toString()).stream().filter(filter.and(PREDICATE_NO_DOTS))
+ .collect(Collectors.toList());
+ } catch (SftpException e) {
+ throw new SSHException("Failed to list remote directory " + path + '.', e);
+ }
}
}
diff --git a/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/util/SSHUtil.java b/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/util/SSHUtil.java
index 93c9b0eff4b..2817bd5e2b3 100644
--- a/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/util/SSHUtil.java
+++ b/nucleus/cluster/ssh/src/main/java/org/glassfish/cluster/ssh/util/SSHUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -17,14 +17,11 @@
package org.glassfish.cluster.ssh.util;
-import com.jcraft.jsch.Session;
import com.sun.enterprise.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import org.glassfish.api.admin.CommandException;
@@ -36,48 +33,15 @@
*/
public class SSHUtil {
- private static final List activeConnections = new ArrayList<>();
+ /** List of supported SSH key file names */
+ public static final List SSH_KEY_FILE_NAMES = List.of("id_rsa", "id_dsa", "id_ecdsa", "identity");
/**
- * Registers a connection for cleanup when the plugin is stopped.
- *
- * @param session The connection.
- */
- public static synchronized void register(Session session) {
- if (!activeConnections.contains(session)) {
- activeConnections.add(session);
- }
- }
-
-
- /**
- * Unregisters a connection for cleanup when the plugin is stopped.
- *
- * @param session The connection.
- */
- public static synchronized void unregister(Session session) {
- session.disconnect();
- activeConnections.remove(session);
- }
-
-
- /**
- * Convert empty string to null.
- */
- public static String checkString(String s) {
- if (s == null || s.isEmpty()) {
- return null;
- }
- return s;
- }
-
-
- /**
- * @return null or id_rsa/id_dsa/identity at user's home directory
+ * @return null or one of {@link #SSH_KEY_FILE_NAMES} at user's home directory
*/
public static File getExistingKeyFile() {
Path h = FileUtils.USER_HOME.toPath();
- for (String keyName : Arrays.asList("id_rsa", "id_dsa", "identity")) {
+ for (String keyName : SSH_KEY_FILE_NAMES) {
File f = h.resolve(Path.of(".ssh", keyName)).toFile();
if (f.exists()) {
return f;
@@ -87,43 +51,46 @@ public static File getExistingKeyFile() {
}
+ /**
+ * @return .ssh/id_rsa in the current user's home directory.
+ */
public static File getDefaultKeyFile() {
return FileUtils.USER_HOME.toPath().resolve(Path.of(".ssh", "id_rsa")).toFile();
}
/**
* Simple method to validate an encrypted key file
- * @return true|false
+ *
+ * @param keyFile
+ * @return true if the key file is encrypted using standard format
* @throws CommandException
*/
public static boolean isEncryptedKey(File keyFile) throws CommandException {
- boolean res = false;
try {
String f = FileUtils.readSmallFile(keyFile, ISO_8859_1).trim();
- if (f.startsWith("-----BEGIN ") && f.contains("ENCRYPTED")
- && f.endsWith(" PRIVATE KEY-----")) {
- res=true;
+ if (f.startsWith("-----BEGIN ") && f.contains("ENCRYPTED") && f.endsWith(" PRIVATE KEY-----")) {
+ return true;
}
- }
- catch (IOException ioe) {
+ return false;
+ } catch (IOException ioe) {
throw new CommandException(Strings.get("error.parsing.key", keyFile, ioe.getMessage()), ioe);
}
- return res;
}
/**
- * This method validates either private or public key file. In case of private
- * key, it parses the key file contents to verify if it indeed contains a key
- * @param file the key file
- * @return success if file exists, false otherwise
+ * This method validates either private or public key file.
+ * In case of private key, it parses the key file contents to verify if it indeed contains a key
+ *
+ * @param file the key file
+ * @throws CommandException
*/
- public static boolean validateKeyFile(File file) throws CommandException {
+ public static void validateKeyFile(File file) throws CommandException {
if (!file.exists()) {
throw new CommandException(Strings.get("key.does.not.exist", file));
}
if (!file.getName().endsWith(".pub")) {
- String key = null;
+ final String key;
try {
key = FileUtils.readSmallFile(file, ISO_8859_1).trim();
} catch (IOException ioe) {
@@ -133,6 +100,5 @@ public static boolean validateKeyFile(File file) throws CommandException {
throw new CommandException(Strings.get("invalid.key.file", file));
}
}
- return true;
}
}
diff --git a/nucleus/common/common-util/src/main/java/com/sun/common/util/logging/LoggingConfigImpl.java b/nucleus/common/common-util/src/main/java/com/sun/common/util/logging/LoggingConfigImpl.java
index e95e918aa29..c89bb782bb7 100644
--- a/nucleus/common/common-util/src/main/java/com/sun/common/util/logging/LoggingConfigImpl.java
+++ b/nucleus/common/common-util/src/main/java/com/sun/common/util/logging/LoggingConfigImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -606,8 +606,7 @@ private void addDirectory(ZipOutputStream zout, File fileSource, int ignoreLengt
}
/**
- * Return a logging file details in the logging.properties file.
- *
+ * @return a logging file path from the logging.properties file.
* @throws IOException If an I/O error occurs
*/
public synchronized String getLoggingFileDetails() throws IOException {
@@ -631,7 +630,7 @@ public synchronized String getLoggingFileDetails() throws IOException {
/**
- * @return a logging file details in the logging.properties file for given target.
+ * @return a logging file path in the logging.properties file for given target.
* @throws IOException If an I/O error occurs
*/
public synchronized String getLoggingFileDetails(String targetConfigName) throws IOException {
diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/InstanceDirs.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/InstanceDirs.java
index 5dfa7aa6452..97373a306fd 100644
--- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/InstanceDirs.java
+++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/InstanceDirs.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ * Copyright (c) 2024, 2025 Contributors to the Eclipse Foundation.
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -65,6 +65,7 @@ public InstanceDirs(File instanceDir) throws IOException {
/**
* This constructor handles 0, 1, 2 or 3 null args.
* It is smart enough to figure out many defaults.
+ *
* @param nodeDirParentPath E.g. install-dir/nodes
* @param nodeDirName E.g. install-dir/nodes/localhost
* @param instanceName E.g. i1
@@ -74,67 +75,46 @@ public InstanceDirs(String nodeDirParentPath, String nodeDirName, String instanc
nodeDirParentPath = getNodeDirRootDefault();
}
- File nodeDirParent = new File(nodeDirParentPath);
-
+ final File nodeDirParent = new File(nodeDirParentPath);
if (!nodeDirParent.isDirectory()) {
dirs = null;
- throw new IOException(Strings.get("InstanceDirs.noNodeParent"));
+ throw new IOException(Strings.get("InstanceDirs.noNodeParent", nodeDirParent));
}
- File nodeDir;
-
+ final File nodeDir;
if (StringUtils.ok(nodeDirName)) {
nodeDir = new File(nodeDirParent, nodeDirName);
} else {
nodeDir = getTheOneAndOnlyNode(nodeDirParent);
}
-
if (!nodeDir.isDirectory()) {
dirs = null;
throw new IOException(Strings.get("InstanceDirs.badNodeDir", nodeDir));
}
- File instanceDir;
-
+ final File instanceDir;
if (StringUtils.ok(instanceName)) {
instanceDir = new File(nodeDir, instanceName);
} else {
instanceDir = getTheOneAndOnlyInstance(nodeDir);
}
-
if (!instanceDir.isDirectory()) {
dirs = null;
throw new IOException(Strings.get("InstanceDirs.badInstanceDir", instanceDir));
}
-
- // whew!!!
-
dirs = new ServerDirs(instanceDir);
}
- private File getTheOneAndOnlyNode(File parent) throws IOException {
- // look for subdirs in the parent dir -- there must be one and only one
-
- File[] files = parent.listFiles(new FileFilter() {
-
- @Override
- public boolean accept(File f) {
- return f != null && f.isDirectory();
- }
- });
- // ERROR: No node dirs
+ /** Look for subdirs in the parent dir -- there must be one and only one */
+ private File getTheOneAndOnlyNode(File parent) throws IOException {
+ File[] files = parent.listFiles(f -> f != null && f.isDirectory());
if (files == null || files.length < 1) {
- throw new IOException(
- Strings.get("InstanceDirs.noNodes", parent));
+ throw new IOException(Strings.get("InstanceDirs.noNodes", parent));
}
- // ERROR: more than one node dir child
if (files.length > 1) {
- throw new IOException(
- Strings.get("InstanceDirs.tooManyNodes", parent, files.length));
+ throw new IOException(Strings.get("InstanceDirs.tooManyNodes", parent, files.length));
}
-
- // the usual case -- one node dir child
return files[0];
}
diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/LocalStrings.properties b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/LocalStrings.properties
index 47de09275a6..e08e7dd54d2 100644
--- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/LocalStrings.properties
+++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/io/LocalStrings.properties
@@ -22,7 +22,7 @@ ServerDirs.invalidState=This method call is illegal because the object is in an
ServerDirs.nullArg=Null arguments are not allowed in {0}
InstanceDirs.noGrandParent=Server instances are required to have a grandparent \
directory for backward compatability. Here is the instance's directory: {0}
-InstanceDirs.noNodeParent=No node parent directory found.
+InstanceDirs.noNodeParent=No node parent directory found: {0}
InstanceDirs.tooManyNodes=There is more than one directory ({1}) in the node parent directory ({0}). Cannot choose a default node.
InstanceDirs.noNodes=There are no nodes in {0}.
InstanceDirs.badNodeDir=The specified node directory doesn''t exist: {0}
diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/net/NetUtils.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/net/NetUtils.java
index ee3730dab24..dcd124cc0db 100644
--- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/net/NetUtils.java
+++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/net/NetUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ * Copyright (c) 2024, 2025 Contributors to the Eclipse Foundation.
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
+import java.util.function.Predicate;
public final class NetUtils {
@@ -475,6 +476,24 @@ public static String getCanonicalHostName() throws UnknownHostException {
return defaultHostname;
}
+ // If the DNS is inconsistent between domain hosts, it is sometimes better to use
+ // any reachable public IP address. This check is not perfect - however usually if
+ // the host name doesn't contain any dots, it is already a bad name for wider networks.
+ if (!hostname.contains(".")) {
+ ThrowingPredicate isLoopback = NetworkInterface::isLoopback;
+ try {
+ String host = NetworkInterface.networkInterfaces().filter(Predicate.not(isLoopback))
+ .flatMap(NetworkInterface::inetAddresses)
+ .map(InetAddress::getHostAddress)
+ .filter(name -> name.indexOf('.') > 0)
+ .findFirst().orElse(hostname);
+ return host;
+ } catch (SocketException e) {
+ e.printStackTrace();
+ }
+
+ }
+
return hostname;
}
@@ -495,4 +514,22 @@ private static String trimIP(String ip) {
public enum PortAvailability {
illegalNumber, noPermission, inUse, unknown, OK
}
+
+
+ @FunctionalInterface
+ private static interface ThrowingPredicate extends Predicate {
+
+ boolean throwing(T object) throws Exception;
+
+ @Override
+ public default boolean test(T object) {
+ try {
+ return throwing(object);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
}
diff --git a/nucleus/common/common-util/src/test/resources/clusters1.xml b/nucleus/common/common-util/src/test/resources/clusters1.xml
index 50728ddf616..d8f6838799e 100644
--- a/nucleus/common/common-util/src/test/resources/clusters1.xml
+++ b/nucleus/common/common-util/src/test/resources/clusters1.xml
@@ -341,7 +341,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -507,7 +507,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -673,7 +673,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -839,7 +839,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -1005,7 +1005,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
diff --git a/nucleus/common/common-util/src/test/resources/manysysprops.xml b/nucleus/common/common-util/src/test/resources/manysysprops.xml
index f63d23ed193..de63f982b00 100644
--- a/nucleus/common/common-util/src/test/resources/manysysprops.xml
+++ b/nucleus/common/common-util/src/test/resources/manysysprops.xml
@@ -346,7 +346,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -515,7 +515,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -681,7 +681,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -847,7 +847,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
@@ -1013,7 +1013,7 @@
-Dosgi.shell.telnet.port=${OSGI_SHELL_TELNET_PORT}
-Dosgi.shell.telnet.maxconn=1
-Dosgi.shell.telnet.ip=127.0.0.1
- -Dgosh.args=--noshutdown -c noop=true
+ -Dgosh.args=--nointeractive
-Dfelix.fileinstall.dir=${com.sun.aas.installRoot}/modules/autostart/
-Dfelix.fileinstall.poll=5000
-Dfelix.fileinstall.log.level=3
diff --git a/nucleus/common/internal-api/src/main/java/org/glassfish/internal/api/RelativePathResolver.java b/nucleus/common/internal-api/src/main/java/org/glassfish/internal/api/RelativePathResolver.java
index 5da7e56a354..19a4da8d7b4 100644
--- a/nucleus/common/internal-api/src/main/java/org/glassfish/internal/api/RelativePathResolver.java
+++ b/nucleus/common/internal-api/src/main/java/org/glassfish/internal/api/RelativePathResolver.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2024, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -21,13 +22,8 @@
import com.sun.enterprise.util.i18n.StringManagerBase;
import java.io.File;
-import java.io.IOException;
import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
import java.util.logging.Level;
-import java.util.logging.Logger;
import org.glassfish.api.admin.PasswordAliasStore;
@@ -48,8 +44,6 @@
*/
public class RelativePathResolver {
- private static Logger _logger = null;
-
private static RelativePathResolver _instance = null;
private static final String ALIAS_TOKEN = "ALIAS";
@@ -99,8 +93,8 @@ public String unresolve(String path, String[] propNames) {
//assumption is that the File class can convert this to an OS
//dependent path separator (e.g. \\ on windows).
path = path.replace(File.separatorChar, '/');
- for (int i = 0; i < propNames.length; i++) {
- propVal = getPropertyValue(propNames[i], true);
+ for (String propName : propNames) {
+ propVal = getPropertyValue(propName, true);
if (propVal != null) {
//All paths returned will contain / as the separator. This will allow
//all comparison to be done using / as the separator
@@ -108,13 +102,13 @@ public String unresolve(String path, String[] propNames) {
startIdx = path.indexOf(propVal);
if (startIdx >= 0) {
path = path.substring(0, startIdx) +
- "${" + propNames[i] + "}" +
+ "${" + propName + "}" +
path.substring(startIdx + propVal.length());
}
} else {
InternalLoggerInfo.getLogger().log(Level.SEVERE,
InternalLoggerInfo.unknownProperty,
- new Object[] {propNames[i], path});
+ new Object[] {propName, path});
}
}
}
@@ -162,8 +156,9 @@ static public String getAlias(String propName)
int lastIdx = propName.length() - 1;
if (lastIdx > 1) {
propName = propName.substring(0,lastIdx);
- if (propName!=null)
- aliasName = propName.trim();
+ if (propName!=null) {
+ aliasName = propName.trim();
+ }
}
}
return aliasName;
@@ -180,8 +175,9 @@ static public String getAlias(String propName)
*/
protected String getPropertyValue(String propName, boolean bIncludingEnvironmentVariables)
{
- if(!bIncludingEnvironmentVariables)
- return null;
+ if(!bIncludingEnvironmentVariables) {
+ return null;
+ }
// Try finding the property as a system property
String result = System.getProperty(propName);
@@ -308,8 +304,8 @@ public static void main(String[] args) {
System.out.println(args[i] + " " + result + " " + resolvePath(result));
}
} else {
- for (int i = 0; i < args.length; i++) {
- System.out.println(args[i] + " " + resolvePath(args[i]));
+ for (String arg : args) {
+ System.out.println(arg + " " + resolvePath(arg));
}
}
}
@@ -333,9 +329,7 @@ public static void main(String[] args) {
* UnrecoverableKeyException if there is an error is opening or
* processing the password store
*/
- public static String getRealPasswordFromAlias(final String at) throws
- KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException,
- UnrecoverableKeyException {
+ public static String getRealPasswordFromAlias(final String at) throws KeyStoreException {
try {
if (at == null || RelativePathResolver.getAlias(at) == null) {
return ( at );
diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/admin/StartServerHook.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/admin/StartServerHook.java
index 34f9a5c73fc..7dfab0580c8 100644
--- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/admin/StartServerHook.java
+++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/admin/StartServerHook.java
@@ -56,6 +56,8 @@ class StartServerShutdownHook extends Thread {
private static final Logger LOG = System.getLogger(StartServerShutdownHook.class.getName());
private static final boolean LOG_RESTART = Boolean.parseBoolean(System.getenv("AS_RESTART_LOGFILES"));
+ private static final Path CFGDIR = new File(System.getProperty("com.sun.aas.instanceRoot"), "config").toPath()
+ .toAbsolutePath();
private static final Path LOGDIR = new File(System.getProperty("com.sun.aas.instanceRoot"), "logs").toPath()
.toAbsolutePath();
private static final Predicate FILTER_OTHER_HOOKS = t -> t.getName().startsWith("GlassFish")
diff --git a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/commands/CollectLogFiles.java b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/commands/CollectLogFiles.java
index 77ea1cbf867..0b5f6c77d4c 100644
--- a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/commands/CollectLogFiles.java
+++ b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/commands/CollectLogFiles.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -33,6 +33,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -111,9 +112,7 @@ public void execute(AdminCommandContext context) {
if (targetServer != null && targetServer.isDas()) {
// This loop if target instance is DAS
- String logFileDetails = "";
- String zipFile = "";
-
+ final String logFileDetails;
try {
// getting log file values from logging.propertie file.
logFileDetails = loggingConfig.getLoggingFileDetails();
@@ -129,12 +128,11 @@ public void execute(AdminCommandContext context) {
File targetDir = makingDirectoryOnDas(targetServer.getName(), report);
try {
-
- String sourceDir = "";
+ final Path sourceDir;
if (logFileDetails.contains("${com.sun.aas.instanceRoot}/logs")) {
- sourceDir = env.getInstanceRoot() + File.separator + "logs";
+ sourceDir = env.getInstanceRoot().toPath().resolve("logs");
} else {
- sourceDir = logFileDetails.substring(0, logFileDetails.lastIndexOf(File.separator));
+ sourceDir = new File(logFileDetails).toPath().getParent();
}
copyLogFilesForLocalhost(sourceDir, targetDir.getAbsolutePath(), report, targetServer.getName());
@@ -148,6 +146,7 @@ public void execute(AdminCommandContext context) {
}
+ String zipFile = null;
try {
String zipFilePath = getZipFilePath().getAbsolutePath();
zipFile = loggingConfig.createZipFile(zipFilePath);
@@ -188,7 +187,7 @@ public void execute(AdminCommandContext context) {
String zipFile = "";
File targetDir = null;
- String logFileDetails = "";
+ String logFileDetails;
try {
// getting log file values from logging.propertie file.
logFileDetails = getInstanceLogFileDirectory(targetServer);
@@ -205,16 +204,15 @@ public void execute(AdminCommandContext context) {
try {
if (node.isLocal()) {
- String sourceDir = getLogDirForLocalNode(logFileDetails, node, serverNode, instanceName);
+ Path sourceDir = getLogDirForLocalNode(logFileDetails, node, serverNode, instanceName);
copyLogFilesForLocalhost(sourceDir, targetDir.getAbsolutePath(), report, instanceName);
} else {
new LogFilterForInstance().downloadAllInstanceLogFiles(habitat, targetServer,
- domain, LOGGER, instanceName, targetDir.getAbsolutePath(), logFileDetails);
+ domain, LOGGER, instanceName, targetDir.toPath(), logFileDetails);
}
- }
- catch (Exception ex) {
- final String errorMsg = localStrings.getLocalString(
- "collectlogfiles.errInstanceDownloading", "Error while downloading log files from {0}.", instanceName);
+ } catch (Exception ex) {
+ final String errorMsg = localStrings.getLocalString("collectlogfiles.errInstanceDownloading",
+ "Error while downloading log files from {0}.", instanceName);
report.setMessage(errorMsg);
report.setFailureCause(ex);
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
@@ -233,10 +231,9 @@ public void execute(AdminCommandContext context) {
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
return;
}
- }
- catch (Exception ex) {
- final String errorMsg = localStrings.getLocalString(
- "collectlogfiles.creatingZip", "Error while creating zip file {0}.", zipFile);
+ } catch (Exception ex) {
+ final String errorMsg = localStrings.getLocalString("collectlogfiles.creatingZip",
+ "Error while creating zip file {0}.", zipFile);
report.setMessage(errorMsg);
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
return;
@@ -260,7 +257,7 @@ public void execute(AdminCommandContext context) {
// code to download server.log file for DAS. Bug fix 16088
- String logFileDetails = "";
+ final String logFileDetails;
try {
// getting log file values from logging.propertie file.
logFileDetails = loggingConfig.getLoggingFileDetails();
@@ -276,15 +273,15 @@ public void execute(AdminCommandContext context) {
targetDir = makingDirectoryOnDas(SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME, report);
try {
- String sourceDir = "";
+ Path sourceDir;
if (logFileDetails.contains("${com.sun.aas.instanceRoot}/logs")) {
- sourceDir = env.getInstanceRoot() + File.separator + "logs";
+ sourceDir = env.getInstanceRoot().toPath().resolve("logs");
} else {
- sourceDir = logFileDetails.substring(0, logFileDetails.lastIndexOf(File.separator));
+ sourceDir = new File(logFileDetails).toPath().getParent();
}
copyLogFilesForLocalhost(sourceDir, targetDir.getAbsolutePath(), report,
- SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME);
+ SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME);
} catch (Exception ex) {
final String errorMsg = localStrings.getLocalString(
"collectlogfiles.errInstanceDownloading", "Error while downloading log files from {0}.", target);
@@ -309,11 +306,10 @@ public void execute(AdminCommandContext context) {
Node node = domain.getNodes().getNode(serverNode);
boolean errorOccur = false;
instanceCount++;
-
- logFileDetails = "";
+ final String logFile;
try {
// getting log file values from logging.propertie file.
- logFileDetails = getInstanceLogFileDirectory(domain.getServerNamed(instanceName));
+ logFile = getInstanceLogFileDirectory(domain.getServerNamed(instanceName));
} catch (Exception ex) {
final String errorMsg = localStrings.getLocalString(
"collectlogfiles.errGettingLogFiles", "Error while getting log file attribute for {0}.", target);
@@ -327,11 +323,11 @@ public void execute(AdminCommandContext context) {
targetDir = makingDirectoryOnDas(instanceName, report);
if (node.isLocal()) {
- String sourceDir = getLogDirForLocalNode(logFileDetails, node, serverNode, instanceName);
+ Path sourceDir = getLogDirForLocalNode(logFile, node, serverNode, instanceName);
copyLogFilesForLocalhost(sourceDir, targetDir.getAbsolutePath(), report, instanceName);
} else {
new LogFilterForInstance().downloadAllInstanceLogFiles(habitat, instance,
- domain, LOGGER, instanceName, targetDir.getAbsolutePath(), logFileDetails);
+ domain, LOGGER, instanceName, targetDir.toPath(), logFile);
}
}
catch (Exception ex) {
@@ -354,7 +350,7 @@ public void execute(AdminCommandContext context) {
String zipFilePath = getZipFilePath().getAbsolutePath();
// Creating zip file and returning zip file absolute path.
zipFile = loggingConfig.createZipFile(zipFilePath);
- if (zipFile == null || new File(zipFile) == null) {
+ if (zipFile == null) {
// Failure during zip
final String errorMsg = localStrings.getLocalString(
"collectlogfiles.creatingZip", "Error while creating zip file {0}.", zipFilePath);
@@ -362,8 +358,7 @@ public void execute(AdminCommandContext context) {
report.setActionExitCode(ActionReport.ExitCode.FAILURE);
return;
}
- }
- catch (Exception ex) {
+ } catch (Exception ex) {
final String errorMsg = localStrings.getLocalString(
"collectlogfiles.creatingZip", "Error while creating zip file {0}.", zipFile);
report.setMessage(errorMsg);
@@ -395,9 +390,9 @@ public void execute(AdminCommandContext context) {
deleteDir(new File(env.getInstanceRoot() + File.separator + "collected-logs" + File.separator + "logs"));
}
- private void copyLogFilesForLocalhost(String sourceDir, String targetDir, ActionReport report, String instanceName) throws IOException {
+ private void copyLogFilesForLocalhost(Path sourceDir, String targetDir, ActionReport report, String instanceName) throws IOException {
// Getting all Log Files
- File logsDir = new File(sourceDir);
+ File logsDir = sourceDir.toFile();
File allLogFileNames[] = logsDir.listFiles();
if (allLogFileNames == null) {
throw new IOException("");
@@ -418,8 +413,7 @@ private void copyLogFilesForLocalhost(String sourceDir, String targetDir, Action
byte[] buffer = new byte[4096];
int bytesRead;
- while ((bytesRead = from.read(buffer)) != -1)
- {
+ while ((bytesRead = from.read(buffer)) != -1) {
to.write(buffer, 0, bytesRead); // write
}
}
@@ -578,22 +572,20 @@ private File makingDirectory(File parent, String path, ActionReport report, Stri
}
}
- private String getLogDirForLocalNode(String instanceLogFileName, Node node, String serverNode, String instanceName) {
- String loggingDir;
- loggingDir = new LogFilterForInstance().getLoggingDirectoryForNode(instanceLogFileName, node, serverNode, instanceName);
-
- File logsDir = new File(loggingDir);
- File allLogFileNames[] = logsDir.listFiles();
-
+ private Path getLogDirForLocalNode(String instanceLogFileDirectory, Node node, String serverNode, String instanceName) {
+ Path loggingDir = new LogFilterForInstance().getLoggingDirectoryForNode(instanceLogFileDirectory, node,
+ serverNode, instanceName);
+ File allLogFileNames[] = loggingDir.toFile().listFiles();
boolean noFileFound = true;
- if (allLogFileNames != null) { // This check for, if directory doesn't present or missing on machine. It happens due to bug 16451
+ if (allLogFileNames != null) { // This check for, if directory doesn't present or missing on
+ // machine. It happens due to bug 16451
for (File file : allLogFileNames) {
String fileName = file.getName();
// code to remove . and .. file which is return
if (file.isFile() && !fileName.equals(".") && !fileName.equals("..") && fileName.contains(".log")
- && !fileName.contains(".log.")) {
+ && !fileName.contains(".log.")) {
noFileFound = false;
break;
}
@@ -602,7 +594,8 @@ private String getLogDirForLocalNode(String instanceLogFileName, Node node, Stri
if (noFileFound) {
// this loop is used when user has changed value for server.log but not restarted the server.
- loggingDir = new LogFilterForInstance().getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileName, node, serverNode, instanceName);
+ loggingDir = new LogFilterForInstance().getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDirectory,
+ node, serverNode, instanceName);
}
diff --git a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilter.java b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilter.java
index d42fda8c4a8..86ae70e04b9 100644
--- a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilter.java
+++ b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -30,6 +30,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.ArrayList;
@@ -47,7 +48,6 @@
import org.glassfish.api.admin.CommandRunner;
import org.glassfish.api.admin.ServerEnvironment;
-import org.glassfish.api.logging.LogLevel;
import org.glassfish.config.support.TranslatedConfigView;
import org.glassfish.hk2.api.ServiceLocator;
import org.jvnet.hk2.annotations.Service;
@@ -68,12 +68,6 @@ public class LogFilter {
// Admin front end.
private static final String RESULTS_ATTRIBUTE = "Results";
- // Load the custom level class for query purpose.
- private static final Level[] GF_CUSTOM_LEVELS = new Level[] {
- LogLevel.ALERT,
- LogLevel.EMERGENCY
- };
-
private static final String NV_SEPARATOR = ";";
static final String[] LOG_LEVELS = {"SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST"};
@@ -246,26 +240,20 @@ public List getInstanceLogFileNames(String instanceName) {
}
}
- /*
- This function is used to get log file details from logging.properties file for given target.
- */
+ /**
+ * This function is used to get log file details from logging.properties file for given target.
+ */
private String getInstanceLogFileDetails(Server targetServer) throws IOException {
-
- String logFileDetailsForServer = "";
- String targetConfigName = "";
-
Cluster clusterForInstance = targetServer.getCluster();
- if (clusterForInstance != null) {
- targetConfigName = clusterForInstance.getConfigRef();
- } else {
+ final String targetConfigName;
+ if (clusterForInstance == null) {
targetConfigName = targetServer.getConfigRef();
+ } else {
+ targetConfigName = clusterForInstance.getConfigRef();
}
- logFileDetailsForServer = loggingConfig.getLoggingFileDetails(targetConfigName);
-
- return logFileDetailsForServer;
-
+ return loggingConfig.getLoggingFileDetails(targetConfigName);
}
/*
@@ -281,41 +269,38 @@ public String getLogFileForGivenTarget(String targetServerName) throws IOExcepti
logFileDetailsForServer = TranslatedConfigView.getTranslatedValue(logFileDetailsForServer).toString();
logFileDetailsForServer = new File(logFileDetailsForServer).getAbsolutePath();
return logFileDetailsForServer;
+ }
+ // getting log file for instance from logging.properties
+ String logFileDetailsForInstance = getInstanceLogFileDetails(targetServer);
+ Node node = domain.getNodes().getNode(serverNode);
+ String loggingDir = "";
+ String loggingFile = "";
+
+ // replacing instanceRoot value if it's there
+ if (logFileDetailsForInstance.contains("${com.sun.aas.instanceRoot}/logs") && node.getNodeDir() != null) {
+ // this code is used if no changes made in logging.properties file
+ loggingDir = node.getNodeDir() + File.separator + serverNode
+ + File.separator + targetServerName;
+ loggingFile = logFileDetailsForInstance.replace("${com.sun.aas.instanceRoot}", loggingDir);
+ } else if (logFileDetailsForInstance.contains("${com.sun.aas.instanceRoot}/logs") && node.getInstallDir() != null) {
+ loggingDir = node.getInstallDir() + File.separator + "glassfish" + File.separator + "nodes"
+ + File.separator + serverNode + File.separator + targetServerName;
+ loggingFile = logFileDetailsForInstance.replace("${com.sun.aas.instanceRoot}", loggingDir);
} else {
- // getting log file for instance from logging.properties
- String logFileDetailsForInstance = getInstanceLogFileDetails(targetServer);
- Node node = domain.getNodes().getNode(serverNode);
- String loggingDir = "";
- String loggingFile = "";
-
- // replacing instanceRoot value if it's there
- if (logFileDetailsForInstance.contains("${com.sun.aas.instanceRoot}/logs") && node.getNodeDir() != null) {
- // this code is used if no changes made in logging.properties file
- loggingDir = node.getNodeDir() + File.separator + serverNode
- + File.separator + targetServerName;
- loggingFile = logFileDetailsForInstance.replace("${com.sun.aas.instanceRoot}", loggingDir);
- } else if (logFileDetailsForInstance.contains("${com.sun.aas.instanceRoot}/logs") && node.getInstallDir() != null) {
- loggingDir = node.getInstallDir() + File.separator + "glassfish" + File.separator + "nodes"
- + File.separator + serverNode + File.separator + targetServerName;
- loggingFile = logFileDetailsForInstance.replace("${com.sun.aas.instanceRoot}", loggingDir);
- } else {
- loggingFile = logFileDetailsForInstance;
- }
-
- if (node.isLocal()) {
- // if local just returning log file to view
- return loggingFile;
- } else {
- // if remote then need to download log file on DAS and returning that log file for view
- String logFileName = logFileDetailsForInstance.substring(logFileDetailsForInstance.lastIndexOf(File.separator) + 1, logFileDetailsForInstance.length());
- File instanceFile = null;
- instanceFile = new LogFilterForInstance().downloadGivenInstanceLogFile(habitat, targetServer, domain, LOGGER,
- targetServerName, env.getInstanceRoot().getAbsolutePath(), logFileName, logFileDetailsForInstance);
-
- return instanceFile.getAbsolutePath();
- }
+ loggingFile = logFileDetailsForInstance;
+ }
+ if (node.isLocal()) {
+ // if local just returning log file to view
+ return loggingFile;
}
+ // if remote then need to download log file on DAS and returning that log file for view
+ String logFileName = logFileDetailsForInstance.substring(logFileDetailsForInstance.lastIndexOf(File.separator) + 1, logFileDetailsForInstance.length());
+ File instanceFile = null;
+ instanceFile = new LogFilterForInstance().downloadGivenInstanceLogFile(habitat, targetServer, domain,
+ targetServerName, env.getInstanceRoot().getAbsolutePath(), logFileName, logFileDetailsForInstance);
+
+ return instanceFile.getAbsolutePath();
}
@@ -336,90 +321,84 @@ public AttributeList getLogRecordsUsingQuery(
requestedCount, fromDate, toDate,
logLevel, onlyLevel, listOfModules,
nameValueMap, anySearch);
- } else {
- // for Instance it's going through this loop. This will use ssh utility to get file from instance machine(remote machine) and
- // store under glassfish/domains/domain1/logs// directory which is used to get LogFile object.
- // Right now user needs to go through this URL to setup and configure ssh http://wikis.sun.com/display/GlassFish/3.1SSHSetup
-
- String serverNode = targetServer.getNodeRef();
- Node node = domain.getNodes().getNode(serverNode);
- String loggingDir = "";
- String instanceLogFileName = "";
- try {
- // getting lof file details for given target.
- instanceLogFileName = getInstanceLogFileDetails(targetServer);
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, LogFacade.ERROR_EXECUTING_LOG_QUERY, e);
- return new AttributeList();
- }
+ }
+
+ String serverNode = targetServer.getNodeRef();
+ Node node = domain.getNodes().getNode(serverNode);
+ Path loggingDir;
+ String instanceLogFileName;
+ try {
+ // getting lof file details for given target.
+ instanceLogFileName = getInstanceLogFileDetails(targetServer);
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, LogFacade.ERROR_EXECUTING_LOG_QUERY, e);
+ return new AttributeList();
+ }
- if (node.isLocal()) {
+ if (node.isLocal()) {
- loggingDir = new LogFilterForInstance().getLoggingDirectoryForNode(instanceLogFileName, node, serverNode, instanceName);
+ loggingDir = new LogFilterForInstance().getLoggingDirectoryForNode(instanceLogFileName, node, serverNode, instanceName);
- File logsDir = new File(loggingDir);
- File allLogFileNames[] = logsDir.listFiles();
+ File logsDir = loggingDir.toFile();
+ File allLogFileNames[] = logsDir.listFiles();
- boolean noFileFound = true;
+ boolean noFileFound = true;
- if (allLogFileNames != null) { // This check for, if directory doesn't present or missing on machine. It happens due to bug 16451
- for (File file : allLogFileNames) {
- String fileName = file.getName();
- // code to remove . and .. file which is return
- if (file.isFile() && !fileName.equals(".") && !fileName.equals("..") && fileName.contains(".log")
- && !fileName.contains(".log.")) {
- noFileFound = false;
- break;
- }
+ if (allLogFileNames != null) { // This check for, if directory doesn't present or missing on machine. It happens due to bug 16451
+ for (File file : allLogFileNames) {
+ String fileName = file.getName();
+ // code to remove . and .. file which is return
+ if (file.isFile() && !fileName.equals(".") && !fileName.equals("..") && fileName.contains(".log")
+ && !fileName.contains(".log.")) {
+ noFileFound = false;
+ break;
}
}
+ }
- if (noFileFound) {
- // this loop is used when user has changed value for server.log but not restarted the server.
- loggingDir = new LogFilterForInstance().getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileName, node, serverNode, instanceName);
+ if (noFileFound) {
+ // this loop is used when user has changed value for server.log but not restarted the server.
+ loggingDir = new LogFilterForInstance().getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileName, node, serverNode, instanceName);
- }
+ }
- instanceLogFile = new File(loggingDir + File.separator + logFileName);
-
- // verifying loggingFile presents or not if not then changing logFileName value to server.log. It means wrong name is coming
- // from GUI to back end code.
- if (!instanceLogFile.exists()) {
- instanceLogFile = new File(loggingDir + File.separator + "server.log");
- } else if (!instanceLogFile.exists()) {
- loggingDir = instanceLogFileName.substring(0, instanceLogFileName.lastIndexOf(File.separator));
- instanceLogFile = new File(loggingDir + File.separator + logFileName);
- if (!instanceLogFile.exists()) {
- instanceLogFile = new File(instanceLogFileName);
- }
- }
+ instanceLogFile = loggingDir.resolve(logFileName).toFile();
+ // verifying loggingFile presents or not if not then changing logFileName value to server.log. It means wrong name is coming
+ // from GUI to back end code.
+ if (!instanceLogFile.exists()) {
+ instanceLogFile = loggingDir.resolve("server.log").toFile();
} else {
- try {
- // this code is used when the node is not local.
- instanceLogFile = new LogFilterForInstance().downloadGivenInstanceLogFile(habitat, targetServer,
- domain, LOGGER, instanceName, env.getInstanceRoot().getAbsolutePath(), logFileName, instanceLogFileName);
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, LogFacade.ERROR_EXECUTING_LOG_QUERY, e);
- return new AttributeList();
+ loggingDir = new File(instanceLogFileName).toPath().getParent();
+ instanceLogFile = new File(loggingDir + File.separator + logFileName);
+ if (!instanceLogFile.exists()) {
+ instanceLogFile = new File(instanceLogFileName);
}
+ }
+ } else {
+ try {
+ // this code is used when the node is not local.
+ instanceLogFile = new LogFilterForInstance().downloadGivenInstanceLogFile(habitat, targetServer,
+ domain, instanceName, env.getInstanceRoot().getAbsolutePath(), logFileName, instanceLogFileName);
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, LogFacade.ERROR_EXECUTING_LOG_QUERY, e);
+ return new AttributeList();
}
+
}
- LogFile logFile = null;
File loggingFileExists = new File(instanceLogFile.getAbsolutePath());
if (!loggingFileExists.exists()) {
LOGGER.log(Level.WARNING, LogFacade.INSTANCE_LOG_FILE_NOT_FOUND, instanceLogFile.getAbsolutePath());
return new AttributeList();
}
- logFile = getLogFile(instanceLogFile.getAbsolutePath());
+ final LogFile logFile = getLogFile(instanceLogFile.getAbsolutePath());
boolean forwd = (forward == null) ? true : forward.booleanValue();
boolean nxt = (next == null) ? true : next.booleanValue();
- long reqCount = (requestedCount == null) ?
- logFile.getIndexSize() : requestedCount.intValue();
+ long reqCount = (requestedCount == null) ? logFile.getIndexSize() : requestedCount.intValue();
long startingRecord;
if (fromRecord == -1) {
// In this case next/previous (before/after) don't mean much since
@@ -669,7 +648,7 @@ public LogFile getLogFile(String fileName) {
protected boolean allChecks(LogFile.LogEntry entry, Instant fromDate, Instant toDate,
String queryLevel, boolean onlyLevel, List listOfModules, Properties nameValueMap, String anySearch) {
if (DEBUG) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append(dateTimeCheck(entry.getLoggedDateTime(), fromDate, toDate));
buf.append(",");
buf.append(levelCheck(entry.getLoggedLevel(), queryLevel, onlyLevel));
diff --git a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilterForInstance.java b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilterForInstance.java
index ee464be34cf..95f91b87657 100644
--- a/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilterForInstance.java
+++ b/nucleus/core/logging/src/main/java/com/sun/enterprise/server/logging/logviewer/backend/LogFilterForInstance.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Contributors to the Eclipse Foundation
+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation
* Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -18,28 +18,22 @@
package com.sun.enterprise.server.logging.logviewer.backend;
import com.jcraft.jsch.ChannelSftp.LsEntry;
-import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpATTRS;
-import com.jcraft.jsch.SftpException;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Node;
import com.sun.enterprise.config.serverbeans.Nodes;
import com.sun.enterprise.config.serverbeans.Server;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
-import java.util.Vector;
import java.util.logging.Logger;
+import org.glassfish.cluster.ssh.launcher.SSHException;
import org.glassfish.cluster.ssh.launcher.SSHLauncher;
+import org.glassfish.cluster.ssh.launcher.SSHSession;
import org.glassfish.cluster.ssh.sftp.SFTPClient;
import org.glassfish.hk2.api.ServiceLocator;
@@ -50,155 +44,122 @@
*/
public class LogFilterForInstance {
- public File downloadGivenInstanceLogFile(ServiceLocator habitat, Server targetServer, Domain domain, Logger logger,
- String instanceName, String domainRoot, String logFileName, String instanceLogFileName)
- throws IOException {
-
- File instanceLogFile = null;
+ public File downloadGivenInstanceLogFile(ServiceLocator habitat, Server targetServer, Domain domain,
+ String instanceName, String domainRoot, String logFileName, String instanceLogFileName) throws IOException {
// method is used from logviewer back end code logfilter.
// for Instance it's going through this loop. This will use ssh utility to get file from instance machine(remote machine) and
// store in domains/domain1/logs/ which is used to get LogFile object.
// Right now user needs to go through this URL to setup and configure ssh http://wikis.sun.com/display/GlassFish/3.1SSHSetup
- SSHLauncher sshL = getSSHL(habitat);
String sNode = targetServer.getNodeRef();
Nodes nodes = domain.getNodes();
Node node = nodes.getNode(sNode);
+ if (!node.getType().equals("SSH")) {
+ return null;
+ }
- if (node.getType().equals("SSH")) {
-
+ final SSHLauncher sshL = new SSHLauncher(node);
+ try (SSHSession session = sshL.openSession(); SFTPClient sftpClient = session.createSFTPClient()) {
+ File logFileDirectoryOnServer = makingDirectory(Path.of(domainRoot, "logs", instanceName));
+ boolean noFileFound = true;
+ Path loggingDir = getLoggingDirectoryForNode(instanceLogFileName, node, sNode, instanceName);
try {
- sshL.init(node, logger);
+ List instanceLogFileNames = sftpClient.ls(loggingDir, this::isAcceptable);
+ if (!instanceLogFileNames.isEmpty()) {
+ noFileFound = false;
+ }
+ } catch (Exception e) {
+ // if directory doesn't present or missing on remote machine. It happens due
+ // to bug 16451
+ noFileFound = true;
+ }
- try (SFTPClient sftpClient = sshL.getSFTPClient()) {
- File logFileDirectoryOnServer = makingDirectory(
- domainRoot + File.separator + "logs" + File.separator + instanceName);
- boolean noFileFound = true;
- String loggingDir = getLoggingDirectoryForNode(instanceLogFileName, node, sNode, instanceName);
- try {
- @SuppressWarnings("unchecked")
- Vector instanceLogFileNames = sftpClient.getSftpChannel().ls(loggingDir);
- for (LsEntry file : instanceLogFileNames) {
- // code to remove . and .. file which is return from sftpclient ls
- // method
- if (isAcceptable(file)) {
- noFileFound = false;
- break;
- }
- }
- } catch (Exception e) {
- // if directory doesn't present or missing on remote machine. It happens due
- // to bug 16451
- noFileFound = true;
- }
+ if (noFileFound) {
+ // this loop is used when user has changed value for server.log but not
+ // restarted the server.
+ loggingDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileName, node, sNode,
+ instanceName);
+ }
- if (noFileFound) {
- // this loop is used when user has changed value for server.log but not
- // restarted the server.
- loggingDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileName, node, sNode, instanceName);
- }
+ Path loggingFile = loggingDir.resolve(logFileName);
+ if (!sftpClient.exists(loggingFile)) {
+ loggingFile = loggingDir.resolve("server.log");
+ } else if (!sftpClient.exists(loggingFile)) {
+ loggingFile = Path.of(instanceLogFileName);
+ }
- String loggingFile = loggingDir + File.separator + logFileName;
- if (!sftpClient.exists(loggingFile)) {
- loggingFile = loggingDir + File.separator + "server.log";
- } else if (!sftpClient.exists(loggingFile)) {
- loggingFile = instanceLogFileName;
- }
+ // creating local file name on DAS
+ long instanceLogFileSize = 0;
+ File instanceLogFile = logFileDirectoryOnServer.toPath().resolve(loggingFile.getFileName()).toFile();
- // creating local file name on DAS
- long instanceLogFileSize = 0;
- instanceLogFile = new File(logFileDirectoryOnServer.getAbsolutePath() + File.separator
- + loggingFile.substring(loggingFile.lastIndexOf(File.separator), loggingFile.length()));
+ // getting size of the file on DAS
+ if (instanceLogFile.exists()) {
+ instanceLogFileSize = instanceLogFile.length();
+ }
- // getting size of the file on DAS
- if (instanceLogFile.exists()) {
- instanceLogFileSize = instanceLogFile.length();
- }
+ // getting size of the file on instance machine
+ SftpATTRS sftpFileAttributes = sftpClient.stat(loggingFile);
+ long fileSizeOnNode = sftpFileAttributes.getSize();
- SftpATTRS sftpFileAttributes = sftpClient._stat(loggingFile);
-
- // getting size of the file on instance machine
- long fileSizeOnNode = sftpFileAttributes.getSize();
-
- // if differ both size then downloading
- if (instanceLogFileSize != fileSizeOnNode) {
- try (InputStream inputStream = sftpClient.getSftpChannel().get(loggingFile);
- BufferedInputStream in = new BufferedInputStream(inputStream);
- FileOutputStream file = new FileOutputStream(instanceLogFile);
- BufferedOutputStream out = new BufferedOutputStream(file)) {
- int i;
- while ((i = in.read()) != -1) {
- out.write(i);
- }
- out.flush();
- }
- }
- }
- } catch (JSchException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
- } catch (SftpException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
+ // if differ both size then downloading
+ if (instanceLogFileSize != fileSizeOnNode) {
+ sftpClient.download(loggingFile, instanceLogFile.toPath());
}
+ return instanceLogFile;
+ } catch (SSHException ex) {
+ throw new SSHException(
+ "Unable to download log file of instance " + instanceName + " from SSH Node " + node.getName() + '.',
+ ex);
}
-
- return instanceLogFile;
-
}
+
+ /**
+ * Download log files of all instances of the node.
+ */
public void downloadAllInstanceLogFiles(ServiceLocator habitat, Server targetServer, Domain domain, Logger logger,
- String instanceName, String tempDirectoryOnServer, String instanceLogFileDirectory)
+ String instanceName, Path tempDirectoryOnServer, String instanceLogFileDirectory)
throws IOException {
- // method is used from collect-log-files command
- // for Instance it's going through this loop. This will use ssh utility to get file from instance machine(remote machine) and
- // store in tempDirectoryOnServer which is used to create zip file.
- // Right now user needs to go through this URL to setup and configure ssh http://wikis.sun.com/display/GlassFish/3.1SSHSetup
- SSHLauncher sshL = getSSHL(habitat);
String sNode = targetServer.getNodeRef();
Nodes nodes = domain.getNodes();
Node node = nodes.getNode(sNode);
if (node.getType().equals("SSH")) {
try {
- sshL.init(node, logger);
-
- List allInstanceLogFileName = getInstanceLogFileNames(habitat, targetServer, domain, logger,
+ List allInstanceLogFileNames = getInstanceLogFileNames(habitat, targetServer, domain, logger,
instanceName, instanceLogFileDirectory);
boolean noFileFound = true;
- String sourceDir = getLoggingDirectoryForNode(instanceLogFileDirectory, node, sNode, instanceName);
- SFTPClient sftpClient = sshL.getSFTPClient();
+ Path sourceDir = getLoggingDirectoryForNode(instanceLogFileDirectory, node, sNode, instanceName);
+ final SSHLauncher sshL = new SSHLauncher(node);
+ try (SSHSession session = sshL.openSession(); SFTPClient sftpClient = session.createSFTPClient()) {
- try {
- @SuppressWarnings("unchecked")
- List instanceLogFileNames = sftpClient.getSftpChannel().ls(sourceDir);
- for (LsEntry file : instanceLogFileNames) {
- // code to remove . and .. file which is return from sftpclient ls method
- if (isAcceptable(file)) {
+ try {
+ List instanceLogFileNames = sftpClient.ls(sourceDir, this::isAcceptable);
+ if (!instanceLogFileNames.isEmpty()) {
noFileFound = false;
- break;
}
+ } catch (Exception e) {
+ // if directory doesn't present or missing on remote machine.
+ // It happens due to bug 16451
+ noFileFound = true;
}
- } catch (Exception e) {
- // if directory doesn't present or missing on remote machine. It happens due to bug 16451
- noFileFound = true;
- }
- if (noFileFound) {
- // this loop is used when user has changed value for server.log but not restarted the server.
- sourceDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDirectory, node, sNode, instanceName);
- }
+ if (noFileFound) {
+ // this loop is used when user has changed value for server.log but not
+ // restarted the server.
+ sourceDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDirectory, node, sNode,
+ instanceName);
+ }
- for (Object element : allInstanceLogFileName) {
- String remoteFileName = sourceDir + File.separator + element;
- InputStream inputStream = sftpClient.getSftpChannel().get(remoteFileName);
- Files.copy(inputStream, Paths.get(tempDirectoryOnServer));
+ for (String fileName : allInstanceLogFileNames) {
+ sftpClient.download(sourceDir.resolve(fileName), tempDirectoryOnServer.resolve(fileName));
+ }
}
- sftpClient.close();
- } catch (JSchException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
- } catch (SftpException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
+ } catch (Exception e) {
+ throw new SSHException("Unable to download log file of instance " + instanceName + " from SSH Node "
+ + node.getName() + '.', e);
}
}
}
@@ -213,9 +174,9 @@ public List getInstanceLogFileNames(ServiceLocator habitat, Server targe
// this code is used when DAS and instances are running on the same machine
if (node.isLocal()) {
- String loggingDir = getLoggingDirectoryForNode(instanceLogFileDetails, node, sNode, instanceName);
+ Path loggingDir = getLoggingDirectoryForNode(instanceLogFileDetails, node, sNode, instanceName);
- File logsDir = new File(loggingDir);
+ File logsDir = loggingDir.toFile();
File allLogFileNames[] = logsDir.listFiles();
boolean noFileFound = true;
@@ -235,7 +196,7 @@ public List getInstanceLogFileNames(ServiceLocator habitat, Server targe
if (noFileFound) {
// this loop is used when user has changed value for server.log but not restarted the server.
loggingDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDetails, node, sNode, instanceName);
- logsDir = new File(loggingDir);
+ logsDir = loggingDir.toFile();
allLogFileNames = logsDir.listFiles();
for (File file : allLogFileNames) {
@@ -248,111 +209,74 @@ public List getInstanceLogFileNames(ServiceLocator habitat, Server targe
}
}
} else if (node.getType().equals("SSH")) {
- try {
// this code is used if DAS and instance are running on different machine
- SSHLauncher sshL = getSSHL(habitat);
- sshL.init(node, logger);
- try (SFTPClient sftpClient = sshL.getSFTPClient()) {
- boolean noFileFound = true;
- String loggingDir = getLoggingDirectoryForNode(instanceLogFileDetails, node, sNode, instanceName);
- try {
- @SuppressWarnings("unchecked")
- Vector instanceLogFileNames = sftpClient.getSftpChannel().ls(loggingDir);
- for (LsEntry file : instanceLogFileNames) {
- // code to remove . and .. file which is return from sftpclient ls method
- if (isAcceptable(file)) {
- instanceLogFileNamesAsString.add(file.getFilename());
- noFileFound = false;
- }
- }
- } catch (Exception ex) {
- // if directory doesn't present or missing on remote machine. It happens due
- // to bug 16451
- noFileFound = true;
- }
-
- if (noFileFound) {
- // this loop is used when user has changed value for server.log but not
- // restarted the server.
- loggingDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDetails, node, sNode,
- instanceName);
- @SuppressWarnings("unchecked")
- Vector instanceLogFileNames = sftpClient.getSftpChannel().ls(loggingDir);
- for (LsEntry file : instanceLogFileNames) {
- // code to remove . and .. file which is return from sftpclient ls
- // method
- if (isAcceptable(file)) {
- instanceLogFileNamesAsString.add(file.getFilename());
- }
- }
+ SSHLauncher sshL = new SSHLauncher(node);
+ try (SSHSession session = sshL.openSession(); SFTPClient sftpClient = session.createSFTPClient()) {
+ boolean noFileFound = true;
+ Path loggingDir = getLoggingDirectoryForNode(instanceLogFileDetails, node, sNode, instanceName);
+ try {
+ List instanceLogFileNames = sftpClient.ls(loggingDir, this::isAcceptable);
+ for (String file : instanceLogFileNames) {
+ instanceLogFileNamesAsString.add(file);
+ noFileFound = false;
}
+ } catch (Exception ex) {
+ // if directory doesn't present or missing on remote machine. It happens due
+ // to bug 16451
+ noFileFound = true;
+ }
+ if (noFileFound) {
+ // this loop is used when user has changed value for server.log but not
+ // restarted the server.
+ loggingDir = getLoggingDirectoryForNodeWhenNoFilesFound(instanceLogFileDetails, node, sNode,
+ instanceName);
+ List instanceLogFileNames = sftpClient.ls(loggingDir, this::isAcceptable);
+ instanceLogFileNamesAsString.addAll(instanceLogFileNames);
}
- } catch (JSchException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
- } catch (SftpException ex) {
- throw new IOException("Unable to download instance log file from SSH Node", ex);
+ } catch (SSHException e) {
+ throw new SSHException("Unable to download log file of instance " + instanceName + " from SSH Node "
+ + node.getName() + '.', e);
}
}
return instanceLogFileNamesAsString;
}
- private SSHLauncher getSSHL(ServiceLocator habitat) {
- return habitat.getService(SSHLauncher.class);
- }
-
- private File makingDirectory(String path) {
- File targetDir = new File(path);
- boolean created = false;
- boolean deleted = false;
+ private File makingDirectory(Path path) {
+ File targetDir = path.toFile();
if (targetDir.exists()) {
- deleted = targetDir.delete();
- if (!deleted) {
+ if (!targetDir.delete()) {
return targetDir;
}
-
}
- created = targetDir.mkdir();
- if (created) {
+ if (targetDir.mkdir()) {
return targetDir;
}
return null;
-
}
- public String getLoggingDirectoryForNode(String instanceLogFileDirectory, Node node, String sNode, String instanceName) {
- String loggingDir = "";
-
+ public Path getLoggingDirectoryForNode(String instanceLogFileDirectory, Node node, String sNode, String instanceName) {
if (instanceLogFileDirectory.contains("${com.sun.aas.instanceRoot}/logs") && node.getNodeDir() != null) {
// this code is used if no changes made in logging.properties file
- loggingDir = node.getNodeDir() + File.separator + sNode
- + File.separator + instanceName + File.separator + "logs";
- } else if (instanceLogFileDirectory.contains("${com.sun.aas.instanceRoot}/logs") && node.getInstallDir() != null) {
- loggingDir = node.getInstallDir() + File.separator + "glassfish" + File.separator + "nodes"
- + File.separator + sNode + File.separator + instanceName + File.separator + "logs";
+ return new File(node.getNodeDir()).toPath().resolve(Path.of(sNode, instanceName, "logs"));
+ } else if (instanceLogFileDirectory.contains("${com.sun.aas.instanceRoot}/logs")
+ && node.getInstallDir() != null) {
+ return new File(node.getInstallDir()).toPath()
+ .resolve(Path.of("glassfish", "nodes", sNode, instanceName, "logs"));
} else {
- loggingDir = instanceLogFileDirectory.substring(0, instanceLogFileDirectory.lastIndexOf(File.separator));
+ return new File(instanceLogFileDirectory).toPath();
}
-
- return loggingDir;
}
- public String getLoggingDirectoryForNodeWhenNoFilesFound(String instanceLogFileDirectory, Node node, String sNode, String instanceName) {
- String loggingDir = "";
-
+ public Path getLoggingDirectoryForNodeWhenNoFilesFound(String instanceLogFileDirectory, Node node, String sNode, String instanceName) {
if (node.getNodeDir() != null) {
// this code is used if no changes made in logging.properties file
- loggingDir = node.getNodeDir() + File.separator + sNode
- + File.separator + instanceName + File.separator + "logs";
+ return new File(node.getNodeDir()).toPath().resolve(Path.of(sNode, instanceName, "logs"));
} else if (node.getInstallDir() != null) {
- loggingDir = node.getInstallDir() + File.separator + "glassfish" + File.separator + "nodes"
- + File.separator + sNode + File.separator + instanceName + File.separator + "logs";
+ return new File(node.getInstallDir()).toPath().resolve(Path.of("glassfish", "nodes", sNode, instanceName, "logs"));
} else {
- loggingDir = instanceLogFileDirectory.substring(0, instanceLogFileDirectory.lastIndexOf(File.separator));
+ return new File(instanceLogFileDirectory).toPath();
}
-
- return loggingDir;
-
}