in pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java [173:334]
public QueryExecutor openConnectionImpl(HostSpec[] hostSpecs, String user, String database,
Properties info) throws SQLException {
SslMode sslMode = SslMode.of(info);
GSSEncMode gssEncMode = GSSEncMode.of(info);
HostRequirement targetServerType;
String targetServerTypeStr = castNonNull(PGProperty.TARGET_SERVER_TYPE.get(info));
try {
targetServerType = HostRequirement.getTargetServerType(targetServerTypeStr);
} catch (IllegalArgumentException ex) {
throw new PSQLException(
GT.tr("Invalid targetServerType value: {0}", targetServerTypeStr),
PSQLState.CONNECTION_UNABLE_TO_CONNECT);
}
SocketFactory socketFactory = SocketFactoryFactory.getSocketFactory(info);
HostChooser hostChooser =
HostChooserFactory.createHostChooser(hostSpecs, targetServerType, info);
Iterator<CandidateHost> hostIter = hostChooser.iterator();
Map<HostSpec, HostStatus> knownStates = new HashMap<HostSpec, HostStatus>();
while (hostIter.hasNext()) {
CandidateHost candidateHost = hostIter.next();
HostSpec hostSpec = candidateHost.hostSpec;
LOGGER.log(Level.FINE, "Trying to establish a protocol version 3 connection to {0}", hostSpec);
// Note: per-connect-attempt status map is used here instead of GlobalHostStatusTracker
// for the case when "no good hosts" match (e.g. all the hosts are known as "connectfail")
// In that case, the system tries to connect to each host in order, thus it should not look into
// GlobalHostStatusTracker
HostStatus knownStatus = knownStates.get(hostSpec);
if (knownStatus != null && !candidateHost.targetServerType.allowConnectingTo(knownStatus)) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "Known status of host {0} is {1}, and required status was {2}. Will try next host",
new Object[]{hostSpec, knownStatus, candidateHost.targetServerType});
}
continue;
}
//
// Establish a connection.
//
PGStream newStream = null;
try {
try {
newStream = tryConnect(user, database, info, socketFactory, hostSpec, sslMode, gssEncMode);
} catch (SQLException e) {
if (sslMode == SslMode.PREFER
&& PSQLState.INVALID_AUTHORIZATION_SPECIFICATION.getState().equals(e.getSQLState())) {
// Try non-SSL connection to cover case like "non-ssl only db"
// Note: PREFER allows loss of encryption, so no significant harm is made
Throwable ex = null;
try {
newStream =
tryConnect(user, database, info, socketFactory, hostSpec, SslMode.DISABLE,gssEncMode);
LOGGER.log(Level.FINE, "Downgraded to non-encrypted connection for host {0}",
hostSpec);
} catch (SQLException ee) {
ex = ee;
} catch (IOException ee) {
ex = ee; // Can't use multi-catch in Java 6 :(
}
if (ex != null) {
log(Level.FINE, "sslMode==PREFER, however non-SSL connection failed as well", ex);
// non-SSL failed as well, so re-throw original exception
// Add non-SSL exception as suppressed
e.addSuppressed(ex);
throw e;
}
} else if (sslMode == SslMode.ALLOW
&& PSQLState.INVALID_AUTHORIZATION_SPECIFICATION.getState().equals(e.getSQLState())) {
// Try using SSL
Throwable ex = null;
try {
newStream =
tryConnect(user, database, info, socketFactory, hostSpec, SslMode.REQUIRE, gssEncMode);
LOGGER.log(Level.FINE, "Upgraded to encrypted connection for host {0}",
hostSpec);
} catch (SQLException ee) {
ex = ee;
} catch (IOException ee) {
ex = ee; // Can't use multi-catch in Java 6 :(
}
if (ex != null) {
log(Level.FINE, "sslMode==ALLOW, however SSL connection failed as well", ex);
// non-SSL failed as well, so re-throw original exception
// Add SSL exception as suppressed
e.addSuppressed(ex);
throw e;
}
} else {
throw e;
}
}
int cancelSignalTimeout = PGProperty.CANCEL_SIGNAL_TIMEOUT.getInt(info) * 1000;
// CheckerFramework can't infer newStream is non-nullable
castNonNull(newStream);
// Do final startup.
QueryExecutor queryExecutor = new QueryExecutorImpl(newStream, user, database,
cancelSignalTimeout, info);
// Check Primary or Secondary
HostStatus hostStatus = HostStatus.ConnectOK;
if (candidateHost.targetServerType != HostRequirement.any) {
hostStatus = isPrimary(queryExecutor) ? HostStatus.Primary : HostStatus.Secondary;
}
GlobalHostStatusTracker.reportHostStatus(hostSpec, hostStatus);
knownStates.put(hostSpec, hostStatus);
if (!candidateHost.targetServerType.allowConnectingTo(hostStatus)) {
queryExecutor.close();
continue;
}
runInitialQueries(queryExecutor, info);
// And we're done.
return queryExecutor;
} catch (ConnectException cex) {
// Added by Peter Mount <peter@retep.org.uk>
// ConnectException is thrown when the connection cannot be made.
// we trap this an return a more meaningful message for the end user
GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
knownStates.put(hostSpec, HostStatus.ConnectFail);
if (hostIter.hasNext()) {
log(Level.FINE, "ConnectException occurred while connecting to {0}", cex, hostSpec);
// still more addresses to try
continue;
}
throw new PSQLException(GT.tr(
"Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.",
hostSpec), PSQLState.CONNECTION_UNABLE_TO_CONNECT, cex);
} catch (IOException ioe) {
closeStream(newStream);
GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
knownStates.put(hostSpec, HostStatus.ConnectFail);
if (hostIter.hasNext()) {
log(Level.FINE, "IOException occurred while connecting to {0}", ioe, hostSpec);
// still more addresses to try
continue;
}
throw new PSQLException(GT.tr("The connection attempt failed."),
PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
} catch (SQLException se) {
closeStream(newStream);
GlobalHostStatusTracker.reportHostStatus(hostSpec, HostStatus.ConnectFail);
knownStates.put(hostSpec, HostStatus.ConnectFail);
if (hostIter.hasNext()) {
log(Level.FINE, "SQLException occurred while connecting to {0}", se, hostSpec);
// still more addresses to try
continue;
}
throw se;
}
}
throw new PSQLException(GT
.tr("Could not find a server with specified targetServerType: {0}", targetServerType),
PSQLState.CONNECTION_UNABLE_TO_CONNECT);
}