Skip to content
Open
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 @@ -36,6 +36,7 @@ public CliProcessWrapper(boolean modular){
private Process cliProcess;
private volatile StringBuffer cliOutputBuffer = new StringBuffer();
private BufferedWriter bufferedWriter = null;
private boolean idleInput = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable must be volatile, otherwise there will not be visibility guarantees between the three threads that modify/read it


/**
* Clear current output buffer
Expand Down Expand Up @@ -178,7 +179,7 @@ public void destroyProcess() {
*/
public String getCurrentPrompt(){
if(cliOutputBuffer.toString().contains("\n")) {
return cliOutputBuffer.toString().substring(cliOutputBuffer.toString().lastIndexOf("\n")+1);
return cliOutputBuffer.substring(cliOutputBuffer.toString().lastIndexOf("\n")+1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please always apply the standard code formatting, otherwise in the future applying the format will generate changes

Comment on lines 181 to +182

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What hapends if after reading"cliOutputBuffer.toString().contains("\n")" the cliOutputBuffer changes in between before executing the return?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method is called after we made sure the buffer shouldn't be changing. But even if it changed it should still be returning the last line of the output, worse case lastIndexOf("\n")+1 is 0.

}else{
return cliOutputBuffer.toString();
}
Expand Down Expand Up @@ -279,18 +280,15 @@ private boolean outputHasPrompt(String prompt){
if (buffer.startsWith("Exception")){
throw new RuntimeException(buffer);
}
if(buffer.contains("\n")) {
if (prompt == null) {
return buffer.substring(buffer.lastIndexOf("\n")+1).matches(".*[\\[].*[\\]].*");
} else {
return buffer.substring(buffer.lastIndexOf("\n")).contains(prompt);
}
}else{
if (prompt == null) {
return buffer.matches(".*[\\[].*[\\]].*");
} else {
return buffer.contains(prompt);
}
if (!idleInput) {
return false;
}

String currentPrompt = getCurrentPrompt();
if (prompt == null) {
return currentPrompt.matches(".*[\\[].*[\\]].*");
} else {
return currentPrompt.contains(prompt);
}
}

Expand All @@ -302,6 +300,8 @@ public CliResultsReader(InputStream inputStream){
cliStream = inputStream;
}

int idleCounter = 0;

@Override
public void run() {
try(java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(cliStream, StandardCharsets.UTF_8))){
Expand All @@ -310,6 +310,19 @@ public void run() {
// While the next character is a valid character and isn't null
while ((intCharacter = br.read()) != -1 && (character = (char)intCharacter) != null) {
cliOutputBuffer.append(character);
if (character == '\n') {
idleInput = false;
idleCounter = 0;
}

// reader is not ready either after processing CLI output = idle prompt (this happens at most once per line)
// or intermittently while processing user input - this has to be ignored
if (!br.ready()) {
idleInput = idleCounter <= 1;
idleCounter++;
} else {
idleInput = false;
}
Comment on lines +320 to +325

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not get the behaviour of this logic. Could you explain it here? , why "idleCounter <= 1" and why you do it if the buffer is not ready?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the process will echo the user input, and here what you want to do is to signal when this echo has not finished yet. Also, I understand that the server output is processed as an stream, and I guess this is the main issue.

Correct me if I am wrong, each echo will be following by a new line, and in that moment the output of the command result will start, probably it will contain several lines.

I do not get how this will help with this:

https://github.com/michpetrov/wildfly-core/blob/wfcore-7623-2/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessWrapper.java#L232

which seems to be the main issue since outputHasPrompt could be invoked after the input was echoed by the server but the full command output is not fully processed yet.

Would not be easier to use a producer consumer pattern on the threads that reads the std out / err and produce line by line?, consumers will wait until a line is ready, consumers will not read lines in the middle of processing. In general, looks like that sharing the cliOutputBuffer between the CliResultsReader threads and the main thread is quite problematic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it is the waitForPrompt method waits until the last line of the buffer contains a certain string. What I want to do is wait until we're sure nothing more will be added to the buffer before checking the strings. Another solution would be to specify the exact string we're expecting but I would have to go through every test.

If we were reading the output line by line we couldn't check the current prompt since that's not a complete line yet. And it would still need some mechanism to tell us the producer is finished producing, wouldn't it?

if reader.ready() returns false that generally means there are no more characters to be read from the stream, unfortunately that's not always the case

If we start with e.g.

[standalone@localhost] 

br.read() is blocking, and idleInput is true (because br.ready() was false in the last loop)

We now execute a command, idleInput is set to false as soon as we read a new character.
We print the command, then the output.
Now a prompt starts printing, we get br.ready() == false at the start of the line (counter = 0) but I think this only happens when we color the output, when the prompt is finished we get it again (counter = 1), now the thread is blocking again, idleInput == true and we can check the string.

}
} catch (IOException e) {
fail("Failed to read process output or error streams: " + e.getLocalizedMessage());
Expand Down
Loading