org.jboss.remoting3.ConnectionInfo Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.remoting3;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import org.wildfly.common.Assert;
import org.wildfly.security.auth.client.AuthenticationConfiguration;
import org.xnio.Cancellable;
import org.xnio.FutureResult;
import org.xnio.IoFuture;
import org.xnio.OptionMap;
/**
* @author David M. Lloyd
*/
final class ConnectionInfo {
final OptionMap connectOptions;
State state = new None();
private static final IoFuture RETRY = new EmptyIoFuture();
ConnectionInfo(final OptionMap connectOptions) {
this.connectOptions = connectOptions;
}
IoFuture getConnection(final EndpointImpl endpoint, ConnectionKey key, AuthenticationConfiguration authenticationConfiguration, boolean doConnect) {
IoFuture result;
State state;
do {
synchronized (this) {
state = this.state;
}
result = state.getConnection(endpoint, key, authenticationConfiguration, doConnect);
} while (result == RETRY);
return result;
}
void connectionClosed(AuthenticationConfiguration authenticationConfiguration, final FutureResult futureResult) {
State state;
do {
synchronized (this) {
state = this.state;
}
} while (! state.connectionClosed(authenticationConfiguration, futureResult));
}
abstract static class State {
abstract IoFuture getConnection(EndpointImpl endpoint, ConnectionKey key, AuthenticationConfiguration authenticationConfiguration, boolean doConnect);
abstract boolean connectionClosed(AuthenticationConfiguration authenticationConfiguration, FutureResult futureResult);
}
final class None extends State {
IoFuture getConnection(final EndpointImpl endpoint, final ConnectionKey key, final AuthenticationConfiguration authenticationConfiguration, boolean doConnect) {
if (! doConnect) return null;
State oldState;
synchronized (ConnectionInfo.this) {
oldState = state;
if (oldState == this) {
final IoFuture attempt = endpoint.connect(key.getRealUri(), null, connectOptions, key.getSslContext(), authenticationConfiguration);
final MaybeShared maybeShared = new MaybeShared(authenticationConfiguration, attempt);
final FutureResult futureResult = new FutureResult<>();
splice(futureResult, attempt, authenticationConfiguration);
state = maybeShared;
attempt.addNotifier(new IoFuture.HandlingNotifier() {
public void handleCancelled(final Void attachment) {
final ConnectionInfo outer = ConnectionInfo.this;
synchronized (outer) {
assert state == maybeShared;
state = None.this;
synchronized (maybeShared.pendingAttempts) {
for (Map.Entry> pendingAttempt : maybeShared.pendingAttempts.entrySet()) {
final AuthenticationConfiguration pendingAuthenticationConfiguration = pendingAttempt.getKey();
final FutureResult pendingFutureResult = pendingAttempt.getValue();
final IoFuture realAttempt = outer.getConnection(endpoint, key, pendingAuthenticationConfiguration, true);
splice(pendingFutureResult, realAttempt, pendingAuthenticationConfiguration);
}
}
}
}
public void handleFailed(final IOException exception, final Void attachment) {
final ConnectionInfo outer = ConnectionInfo.this;
synchronized (outer) {
assert state == maybeShared;
state = None.this;
synchronized (maybeShared.pendingAttempts) {
for (Map.Entry> pendingAttempt : maybeShared.pendingAttempts.entrySet()) {
final AuthenticationConfiguration pendingAuthenticationConfiguration = pendingAttempt.getKey();
final FutureResult pendingFutureResult = pendingAttempt.getValue();
final IoFuture realAttempt = outer.getConnection(endpoint, key, pendingAuthenticationConfiguration, true);
splice(pendingFutureResult, realAttempt, pendingAuthenticationConfiguration);
}
}
}
}
public void handleDone(final Connection connection, final Void attachment) {
final ConnectionInfo outer = ConnectionInfo.this;
synchronized (outer) {
assert state == maybeShared;
// transition to the next state and resolve all pending attempts
if (connection.supportsRemoteAuth()) {
// shared!
state = new Shared(futureResult, Collections.emptyMap());
} else {
// unsharable :(
state = new NotShared(Collections.singletonMap(authenticationConfiguration, futureResult));
}
synchronized (maybeShared.pendingAttempts) {
for (Map.Entry> pendingAttempt : maybeShared.pendingAttempts.entrySet()) {
final AuthenticationConfiguration pendingAuthenticationConfiguration = pendingAttempt.getKey();
final FutureResult pendingFutureResult = pendingAttempt.getValue();
final IoFuture realAttempt = outer.getConnection(endpoint, key, pendingAuthenticationConfiguration, true);
splice(pendingFutureResult, realAttempt, pendingAuthenticationConfiguration);
}
}
}
}
}, null);
return futureResult.getIoFuture();
}
}
// try again :(
return RETRY;
}
boolean connectionClosed(final AuthenticationConfiguration authenticationConfiguration, final FutureResult futureResult) {
// we can't possibly care; this is probably a bug, even
return true;
}
}
final class MaybeShared extends State {
private final AuthenticationConfiguration authenticationConfiguration;
private final IoFuture attempt;
private final Map> pendingAttempts = new HashMap<>();
MaybeShared(final AuthenticationConfiguration authenticationConfiguration, final IoFuture attempt) {
this.authenticationConfiguration = authenticationConfiguration;
this.attempt = attempt;
}
IoFuture getConnection(final EndpointImpl endpoint, final ConnectionKey key, final AuthenticationConfiguration authenticationConfiguration, boolean doConnect) {
synchronized (pendingAttempts) {
if (authenticationConfiguration.equals(this.authenticationConfiguration)) {
return attempt;
} else {
FutureResult futureResult = pendingAttempts.get(authenticationConfiguration);
if (futureResult != null) {
return futureResult.getIoFuture();
} else {
if (! doConnect) {
return null;
}
futureResult = new FutureResult<>(endpoint.getExecutor());
pendingAttempts.put(authenticationConfiguration, futureResult);
}
assert doConnect;
final IoFuture ioFuture = futureResult.getIoFuture();
final FutureResult finalFutureResult = futureResult;
futureResult.addCancelHandler(new Cancellable() {
public Cancellable cancel() {
finalFutureResult.setCancelled();
return this;
}
});
return ioFuture;
}
}
}
boolean connectionClosed(final AuthenticationConfiguration authenticationConfiguration, final FutureResult futureResult) {
// an early notification... we should be done though, so synchronize and retry
attempt.await();
// try again
return false;
}
}
final class Shared extends State {
private final FutureResult sharedConnection;
private final Map> leftovers;
Shared(final FutureResult sharedConnection, final Map> leftovers) {
this.sharedConnection = sharedConnection;
this.leftovers = leftovers;
}
IoFuture getConnection(final EndpointImpl endpoint, final ConnectionKey key, final AuthenticationConfiguration authenticationConfiguration, boolean doConnect) {
return leftovers.getOrDefault(authenticationConfiguration, sharedConnection).getIoFuture();
}
boolean connectionClosed(final AuthenticationConfiguration authenticationConfiguration, final FutureResult futureResult) {
final State newState;
if (futureResult == sharedConnection) {
// shared connection closed :-(
if (leftovers.isEmpty()) {
newState = new None();
} else {
// not ideal, but also extremely unlikely
newState = new NotShared(leftovers);
}
} else {
final FutureResult mapVal = leftovers.get(authenticationConfiguration);
if (! futureResult.equals(mapVal)) {
// nothing to do
return true;
}
// swap map, maybe
Map> newMap;
if (leftovers.size() == 1) {
newMap = Collections.emptyMap();
} else {
newMap = new HashMap<>(leftovers);
newMap.remove(authenticationConfiguration);
}
newState = new Shared(sharedConnection, newMap);
}
synchronized (ConnectionInfo.this) {
if (state == this) {
state = newState;
return true;
}
}
// try again :(
return false;
}
}
final class NotShared extends State {
private final Map> connections;
NotShared(final Map> connections) {
this.connections = connections;
}
IoFuture getConnection(final EndpointImpl endpoint, final ConnectionKey key, final AuthenticationConfiguration authenticationConfiguration, boolean doConnect) {
final FutureResult future = connections.get(authenticationConfiguration);
if (future != null) {
return future.getIoFuture();
}
if (! doConnect) {
return null;
}
// add a new unshared connection
State oldState;
synchronized (ConnectionInfo.this) {
oldState = ConnectionInfo.this.state;
if (oldState == this) {
final IoFuture attempt = endpoint.connect(key.getRealUri(), null, connectOptions, key.getSslContext(), authenticationConfiguration);
Map> newConnections = new HashMap<>(connections);
final FutureResult futureResult = new FutureResult<>();
splice(futureResult, attempt, authenticationConfiguration);
newConnections.put(authenticationConfiguration, futureResult);
state = new NotShared(newConnections);
return attempt;
}
}
// try again :(
return RETRY;
}
boolean connectionClosed(final AuthenticationConfiguration authenticationConfiguration, final FutureResult futureResult) {
final FutureResult mapVal = connections.get(authenticationConfiguration);
if (! futureResult.equals(mapVal)) {
// nothing to do
return true;
}
// swap map, maybe
final State newState;
if (connections.size() == 1) {
newState = new None();
} else {
Map> newMap = new HashMap<>(connections);
newMap.remove(authenticationConfiguration);
newState = new NotShared(newMap);
}
synchronized (ConnectionInfo.this) {
if (state == this) {
state = newState;
return true;
}
}
// try again :(
return false;
}
}
void splice(FutureResult futureResult, IoFuture realFuture, final AuthenticationConfiguration authConfig) {
// always add in this order
futureResult.addCancelHandler(realFuture);
realFuture.addNotifier(new IoFuture.HandlingNotifier>() {
public void handleCancelled(final FutureResult futureResult1) {
futureResult1.setCancelled();
}
public void handleFailed(final IOException exception, final FutureResult futureResult1) {
futureResult1.setException(exception);
}
public void handleDone(final Connection connection, final FutureResult futureResult1) {
futureResult1.setResult(new ManagedConnection(connection, ConnectionInfo.this, authConfig, futureResult1));
}
}, futureResult);
}
static class EmptyIoFuture implements IoFuture {
public IoFuture cancel() {
throw Assert.unsupported();
}
public Status getStatus() {
throw Assert.unsupported();
}
public Status await() {
throw Assert.unsupported();
}
public Status await(final long time, final TimeUnit timeUnit) {
throw Assert.unsupported();
}
public Status awaitInterruptibly() throws InterruptedException {
throw Assert.unsupported();
}
public Status awaitInterruptibly(final long time, final TimeUnit timeUnit) throws InterruptedException {
throw Assert.unsupported();
}
public Connection get() throws IOException, CancellationException {
throw Assert.unsupported();
}
public Connection getInterruptibly() throws IOException, InterruptedException, CancellationException {
throw Assert.unsupported();
}
public IOException getException() throws IllegalStateException {
throw Assert.unsupported();
}
public IoFuture addNotifier(final Notifier super Connection, A> notifier, final A attachment) {
throw Assert.unsupported();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy