org.jboss.remoting3.ConnectionInfo Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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();
}
}
}