All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 notifier, final A attachment) {
            throw Assert.unsupported();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy