Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
Expand All @@ -38,6 +40,7 @@
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.annotations.internal.MutableForTesting;
Expand Down Expand Up @@ -1094,6 +1097,9 @@ private void write(String message, boolean isError) {
}

protected LineReader createConsoleReader() {
// Check and migrate old history file format before creating LineReader
migrateHistoryFileIfNeeded();

// Create GfshCompleter with our parser to enable TAB completion
GfshCompleter completer = new GfshCompleter(this.parser);

Expand All @@ -1110,6 +1116,50 @@ protected LineReader createConsoleReader() {
return lineReader;
}

/**
* Checks if the history file exists and is in old JLine 2 format.
* If so, backs it up and creates a new empty history file.
*/
private void migrateHistoryFileIfNeeded() {
try {
Path historyPath = Paths.get(getHistoryFileName());
if (!Files.exists(historyPath)) {
return;
}

// Check if file contains old format markers (lines starting with // or complex format)
java.util.List<String> lines = Files.readAllLines(historyPath);
boolean hasOldFormat = false;
for (String line : lines) {
String trimmed = line.trim();
// Old format had // comments and complex history format
if (trimmed.startsWith("//") || trimmed.startsWith("#")) {
hasOldFormat = true;
break;
}
// JLine 3 format should be simple: just command lines or :time:command format
// If we see anything complex, assume old format
if (trimmed.contains(":") && !trimmed.matches("^\\d+:\\d+:.*")) {
// Might be old format - be conservative and migrate
hasOldFormat = true;
break;
}
}

if (hasOldFormat) {
// Backup old history file
Path backupPath = historyPath.getParent()
.resolve(historyPath.getFileName().toString() + ".old");
Files.move(historyPath, backupPath,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
gfshFileLogger.info("Migrated old history file format. Backup saved to: " + backupPath);
}
} catch (IOException e) {
// Ignore - history migration is not critical
gfshFileLogger.warning("Could not migrate history file", e);
}
}

protected void logCommandToOutput(String processedLine) {
String originalString = expandedPropCommandsMap.get(processedLine);
if (originalString != null) {
Expand Down Expand Up @@ -1589,10 +1639,27 @@ protected String expandProperties(final String input) {
@Override
public void run() {
try {
// Initialize terminal and line reader before starting prompt loop
if (!isHeadlessMode) {
initializeTerminal();
createConsoleReader();
}
printBannerAndWelcome();
promptLoop();
} catch (Exception e) {
gfshFileLogger.severe("Error in shell main loop", e);
}
}

/**
* Initializes the JLine 3 Terminal for interactive shell use.
* This must be called before creating the LineReader.
*/
private void initializeTerminal() throws IOException {
if (terminal == null) {
terminal = TerminalBuilder.builder()
.system(true)
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
* Overrides JLine History to add History without newline characters.
* Updated for JLine 3.x: extends DefaultHistory instead of MemoryHistory
*
* <p>
* This implementation handles both old JLine 2 format history files (with timestamp comments)
* and new JLine 3 format. When loading an old format file, it will be converted to the new format.
*
* @since GemFire 7.0
*/
public class GfshHistory extends DefaultHistory {
Expand Down Expand Up @@ -60,6 +64,68 @@ public void setHistoryFilePath(Path path) {
}
}

/**
* Override attach to handle migration from old JLine 2 history format.
* If loading fails due to format issues, we'll backup the old file and start fresh.
*/
@Override
public void attach(org.jline.reader.LineReader reader) {
try {
super.attach(reader);
} catch (Exception e) {
// Check if it's a history file format issue
Throwable cause = e;
while (cause != null) {
if (cause instanceof IllegalArgumentException
&& cause.getMessage() != null
&& cause.getMessage().contains("Bad history file syntax")) {
// Backup old history file and start fresh
migrateOldHistoryFile();
// Try again with clean file
try {
super.attach(reader);
} catch (Exception ex) {
// If still fails, just continue without history
}
return;
}
cause = cause.getCause();
}
// Re-throw if not a history format issue
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
}

/**
* Migrates old JLine 2 format history file to JLine 3 format.
* Backs up the old file and creates a new one with only the valid history entries.
*/
private void migrateOldHistoryFile() {
if (historyFilePath == null || !Files.exists(historyFilePath)) {
return;
}

try {
// Backup old history file
Path backupPath = historyFilePath.getParent()
.resolve(historyFilePath.getFileName().toString() + ".old");
Files.move(historyFilePath, backupPath,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);

// Create new history file with timestamp
try (BufferedWriter writer = Files.newBufferedWriter(historyFilePath,
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
writer.write("# Migrated from old format on " + new java.util.Date());
writer.newLine();
}
} catch (IOException e) {
// Ignore - just start with empty history
}
}

public void addToHistory(String buffer) {
if (isAutoFlush()) {
String redacted = ArgumentRedactor.redact(buffer.trim());
Expand Down
Loading