Skip to content
Draft
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
69 changes: 57 additions & 12 deletions exist-core/src/main/java/org/exist/client/InteractiveClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2059,7 +2059,7 @@ private boolean processCommandLineActions() throws IOException {
* Ask user for login data using gui.
*
* @param props Client properties
* @return FALSE when pressed cancel, TRUE is sucessfull.
* @return FALSE when the login dialog was dismissed, TRUE if login data was provided.
*/
private boolean getGuiLoginData(final Properties props) {
return getGuiLoginData(props, ClientFrame::getLoginData);
Expand All @@ -2078,18 +2078,34 @@ boolean getGuiLoginData(final Properties props, final UnaryOperator<Properties>

/**
* Reusable method for connecting to database. Exits process on failure.
* In GUI mode, retryable errors (e.g. wrong credentials) are rethrown as
* XMLDBException so the caller can prompt the user and retry.
*/
private void connectToDatabase() {
private void connectToDatabase() throws XMLDBException {
try {
connect();
} catch (final Exception cnf) {
if (options.startGUI && frame != null) {
frame.setStatus("Connection to database failed; message: " + cnf.getMessage());
} else {
consoleErr("Connection to database failed; message: " + cnf.getMessage(), cnf);
} catch (final XMLDBException ex) {
handleConnectException(ex, ex);
} catch (final Exception ex) {
handleConnectException(new XMLDBException(ErrorCodes.VENDOR_ERROR, ex.getMessage(), ex), ex);
}
}

private void handleConnectException(final XMLDBException toThrow, final Exception original) throws XMLDBException {
final String message = original.getMessage() != null ? original.getMessage() : original.getClass().getName();
if (options.startGUI && isRetryableError(message)) {
if (frame != null) {
frame.setStatus("Connection to database failed; message: " + message);
}
System.exit(SystemExitCodes.CATCH_ALL_GENERAL_ERROR_EXIT_CODE);
throw toThrow;
}

if (options.startGUI && frame != null) {
frame.setStatus("Connection to database failed; message: " + message);
} else {
consoleErr("Connection to database failed; message: " + message, original);
}
System.exit(SystemExitCodes.CATCH_ALL_GENERAL_ERROR_EXIT_CODE);
}

/**
Expand Down Expand Up @@ -2142,8 +2158,31 @@ public boolean run() throws Exception {
.build();
}

// connect to the db
connectToDatabase();
// connect to the db; in GUI mode retry on bad credentials
if (interactive && options.startGUI) {
boolean connected = false;
while (!connected) {
try {
connectToDatabase();
connected = true;
} catch (final XMLDBException cnf) {
final String message = cnf.getMessage() != null ? cnf.getMessage() : cnf.getClass().getName();
if (isRetryableError(message)) {
ClientFrame.showErrorMessage("Connection to database failed: " + message, cnf);
final boolean haveLoginData = getGuiLoginData(properties);
if (!haveLoginData) {
// user dismissed the login dialog; abort startup
return false;
}
} else {
consoleErr("Connection to database failed; message: " + message, cnf);
System.exit(SystemExitCodes.CATCH_ALL_GENERAL_ERROR_EXIT_CODE);
}
}
}
} else {
connectToDatabase();
}

if (current == null) {
if (options.startGUI && frame != null) {
Expand Down Expand Up @@ -2245,15 +2284,20 @@ final boolean initializeGui() {

final boolean haveLoginData = getGuiLoginData(properties);
if (!haveLoginData) {
// pressed cancel
// user dismissed the login dialog; abort startup
return true;
}

// Need to shutdown ?? ask wolfgang
shutdown(false);

// connect to the db
connectToDatabase();
try {
connectToDatabase();
} catch (final XMLDBException e) {
// connection failed again; loop will re-prompt for credentials
errorMessageReference.set(getExceptionMessage(e));
}

} else if (!errorMessage.isEmpty()) {
// No pattern match, but we have an error. stop here
Expand Down Expand Up @@ -2281,6 +2325,7 @@ final boolean initializeGui() {
boolean isRetryableError(String errorMessage) {
return errorMessage.contains("Invalid password for user") ||
errorMessage.contains("Connection refused: connect") ||
errorMessage.contains("Unauthorized") ||
UNKNOWN_USER_PATTERN.matcher(errorMessage).find();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ void errorln() {
}

@ParameterizedTest
@ValueSource(strings = {"xxxxInvalid password for userXXXX", "YYYYYConnection refused: connectXXXXX", "XXXUser AAAA unknownXXXX"})
@ValueSource(strings = {"xxxxInvalid password for userXXXX", "YYYYYConnection refused: connectXXXXX", "XXXUser AAAA unknownXXXX", "HTTP server returned unexpected status: Unauthorized"})
void isRetryableError(String errorMessage) {
assertThat(client.isRetryableError(errorMessage)).isTrue();
}
Expand Down
Loading