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

io.undertow.server.handlers.proxy.mod_cluster.NodePingUtil 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).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 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 io.undertow.server.handlers.proxy.mod_cluster;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.concurrent.TimeUnit;

import io.undertow.UndertowLogger;
import io.undertow.client.ClientCallback;
import io.undertow.client.ClientConnection;
import io.undertow.client.ClientExchange;
import io.undertow.client.ClientRequest;
import io.undertow.client.UndertowClient;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.proxy.ProxyCallback;
import io.undertow.server.handlers.proxy.ProxyConnection;
import io.undertow.util.Headers;
import io.undertow.util.Methods;
import io.undertow.util.SameThreadExecutor;
import org.xnio.ChannelExceptionHandler;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.IoFuture;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import io.undertow.connector.ByteBufferPool;
import io.undertow.util.WorkerUtils;
import org.xnio.StreamConnection;
import org.xnio.XnioExecutor;
import org.xnio.XnioIoThread;
import org.xnio.XnioWorker;
import org.xnio.channels.StreamSinkChannel;
import org.xnio.channels.StreamSourceChannel;
import org.xnio.ssl.XnioSsl;

/**
 * Utilities to ping a remote node.
 *
 * @author Emanuel Muckenhuber
 */
class NodePingUtil {

    interface PingCallback {

        /**
         * Ping completed.
         */
        void completed();

        /**
         * Ping failed.
         */
        void failed();

    }

    /**
     * Try to open a socket connection to given address.
     *
     * @param address     the socket address
     * @param exchange    the http servers exchange
     * @param callback    the ping callback
     * @param options     the options
     */
    static void pingHost(InetSocketAddress address, HttpServerExchange exchange, PingCallback callback, OptionMap options) {

        final XnioIoThread thread = exchange.getIoThread();
        final XnioWorker worker = thread.getWorker();
        final HostPingTask r = new HostPingTask(address, worker, callback, options);
        // Schedule timeout task
        scheduleCancelTask(exchange.getIoThread(), r, 5, TimeUnit.SECONDS);
        exchange.dispatch(exchange.isInIoThread() ? SameThreadExecutor.INSTANCE : thread, r);
    }

    /**
     * Try to ping a server using the undertow client.
     *
     * @param connection    the connection URI
     * @param callback      the ping callback
     * @param exchange      the http servers exchange
     * @param client        the undertow client
     * @param xnioSsl       the ssl setup
     * @param options       the options
     */
    static void pingHttpClient(URI connection, PingCallback callback, HttpServerExchange exchange, UndertowClient client, XnioSsl xnioSsl, OptionMap options) {

        final XnioIoThread thread = exchange.getIoThread();
        final RequestExchangeListener exchangeListener = new RequestExchangeListener(callback, NodeHealthChecker.NO_CHECK, true);
        final Runnable r = new HttpClientPingTask(connection, exchangeListener, thread, client, xnioSsl, exchange.getConnection().getByteBufferPool(), options);
        exchange.dispatch(exchange.isInIoThread() ? SameThreadExecutor.INSTANCE : thread, r);
        // Schedule timeout task
        scheduleCancelTask(exchange.getIoThread(), exchangeListener, 5, TimeUnit.SECONDS);
    }

    /**
     * Try to ping a node using it's connection pool.
     *
     * @param node        the node
     * @param exchange    the http servers exchange
     * @param callback    the ping callback
     */
    static void pingNode(final Node node, final HttpServerExchange exchange, final PingCallback callback) {
        if (node == null) {
            callback.failed();
            return;
        }

        final int timeout = node.getNodeConfig().getPing();
        exchange.dispatch(exchange.isInIoThread() ? SameThreadExecutor.INSTANCE : exchange.getIoThread(), new Runnable() {
            @Override
            public void run() {
                node.getConnectionPool().connect(null, exchange, new ProxyCallback() {
                    @Override
                    public void completed(final HttpServerExchange exchange, ProxyConnection result) {
                        final RequestExchangeListener exchangeListener = new RequestExchangeListener(callback, NodeHealthChecker.NO_CHECK, false);
                        exchange.dispatch(SameThreadExecutor.INSTANCE, new ConnectionPoolPingTask(result, exchangeListener, node.getNodeConfig().getConnectionURI()));
                        // Schedule timeout task
                        scheduleCancelTask(exchange.getIoThread(), exchangeListener, timeout, TimeUnit.SECONDS);
                    }

                    @Override
                    public void failed(HttpServerExchange exchange) {
                        callback.failed();
                    }

                    @Override
                    public void queuedRequestFailed(HttpServerExchange exchange) {
                        callback.failed();
                    }

                    @Override
                    public void couldNotResolveBackend(HttpServerExchange exchange) {
                        callback.failed();
                    }

                }, timeout, TimeUnit.SECONDS, false);
            }
        });
    }

    /**
     * Internally ping a node. This should probably use the connections from the nodes pool, if there are any available.
     *
     * @param node          the node
     * @param callback      the ping callback
     * @param ioThread      the xnio i/o thread
     * @param bufferPool    the xnio buffer pool
     * @param client        the undertow client
     * @param xnioSsl       the ssl setup
     * @param options       the options
     */
    static void internalPingNode(Node node, PingCallback callback, NodeHealthChecker healthChecker, XnioIoThread ioThread, ByteBufferPool bufferPool, UndertowClient client, XnioSsl xnioSsl, OptionMap options) {

        final URI uri = node.getNodeConfig().getConnectionURI();
        final long timeout = node.getNodeConfig().getPing();
        final RequestExchangeListener exchangeListener = new RequestExchangeListener(callback, healthChecker, true);
        final HttpClientPingTask r = new HttpClientPingTask(uri, exchangeListener, ioThread, client, xnioSsl, bufferPool, options);
        // Schedule timeout task
        scheduleCancelTask(ioThread, exchangeListener, timeout, TimeUnit.SECONDS);
        ioThread.execute(r);
    }

    static class ConnectionPoolPingTask implements Runnable {

        private final RequestExchangeListener exchangeListener;
        private final ProxyConnection proxyConnection;
        private final URI uri;

        ConnectionPoolPingTask(ProxyConnection proxyConnection, RequestExchangeListener exchangeListener, URI uri) {
            this.proxyConnection = proxyConnection;
            this.exchangeListener = exchangeListener;
            this.uri = uri;
        }

        @Override
        public void run() {

            // TODO AJP has a special ping thing

            final ClientRequest request = new ClientRequest();
            request.setMethod(Methods.OPTIONS);
            request.setPath("*");
            request.getRequestHeaders()
                    .add(Headers.USER_AGENT, "mod_cluster ping")
                    .add(Headers.HOST, uri.getHost());
            proxyConnection.getConnection().sendRequest(request, new ClientCallback() {
                @Override
                public void completed(final ClientExchange result) {
                    if (exchangeListener.isDone()) {
                        IoUtils.safeClose(proxyConnection.getConnection());
                        return;
                    }
                    exchangeListener.exchange = result;
                    result.setResponseListener(exchangeListener);
                    try {
                        result.getRequestChannel().shutdownWrites();
                        if (!result.getRequestChannel().flush()) {
                            result.getRequestChannel().getWriteSetter().set(ChannelListeners.flushingChannelListener(null, new ChannelExceptionHandler() {
                                @Override
                                public void handleException(StreamSinkChannel channel, IOException exception) {
                                    IoUtils.safeClose(proxyConnection.getConnection());
                                    exchangeListener.taskFailed();
                                }
                            }));
                            result.getRequestChannel().resumeWrites();
                        }
                    } catch (IOException e) {
                        IoUtils.safeClose(proxyConnection.getConnection());
                        exchangeListener.taskFailed();
                    }
                }

                @Override
                public void failed(IOException e) {
                    exchangeListener.taskFailed();
                }
            });
        }

    }

    static class HostPingTask extends CancellableTask implements Runnable {

        private final InetSocketAddress address;
        private final XnioWorker worker;
        private final OptionMap options;

        HostPingTask(InetSocketAddress address, XnioWorker worker, PingCallback callback, OptionMap options) {
            super(callback);
            this.address = address;
            this.worker = worker;
            this.options = options;
        }

        @Override
        public void run() {
            try {
                final IoFuture future = worker.openStreamConnection(address, new ChannelListener() {
                    @Override
                    public void handleEvent(StreamConnection channel) {
                        IoUtils.safeClose(channel); // Close the channel right away
                    }
                }, options);

                future.addNotifier(new IoFuture.HandlingNotifier() {

                    @Override
                    public void handleCancelled(Void attachment) {
                        cancel();
                    }

                    @Override
                    public void handleFailed(IOException exception, Void attachment) {
                        taskFailed();
                    }

                    @Override
                    public void handleDone(StreamConnection data, Void attachment) {
                        taskCompleted();
                    }
                }, null);

            } catch (Exception e) {
                taskFailed();
            }
        }

    }

    static class HttpClientPingTask implements Runnable {

        private final URI connection;
        private final XnioIoThread thread;
        private final UndertowClient client;
        private final XnioSsl xnioSsl;
        private final ByteBufferPool bufferPool;
        private final OptionMap options;
        private final RequestExchangeListener exchangeListener;

        HttpClientPingTask(URI connection, RequestExchangeListener exchangeListener, XnioIoThread thread, UndertowClient client, XnioSsl xnioSsl, ByteBufferPool bufferPool, OptionMap options) {
            this.connection = connection;
            this.thread = thread;
            this.client = client;
            this.xnioSsl = xnioSsl;
            this.bufferPool = bufferPool;
            this.options = options;
            this.exchangeListener = exchangeListener;
        }

        @Override
        public void run() {

            UndertowLogger.ROOT_LOGGER.httpClientPingTask(connection);
            // TODO AJP has a special ping thing
            client.connect(new ClientCallback() {
                @Override
                public void completed(final ClientConnection clientConnection) {
                    if (exchangeListener.isDone()) {
                        IoUtils.safeClose(clientConnection);
                        return;
                    }

                    final ClientRequest request = new ClientRequest();
                    request.setMethod(Methods.OPTIONS);
                    request.setPath("*");
                    request.getRequestHeaders()
                            .add(Headers.USER_AGENT, "mod_cluster ping")
                            .add(Headers.HOST, connection.getHost());
                    clientConnection.sendRequest(request, new ClientCallback() {

                        @Override
                        public void completed(ClientExchange result) {
                            exchangeListener.exchange = result;
                            if (exchangeListener.isDone()) {
                                return;
                            }
                            result.setResponseListener(exchangeListener);
                            try {
                                result.getRequestChannel().shutdownWrites();
                                if (!result.getRequestChannel().flush()) {
                                    result.getRequestChannel().getWriteSetter().set(ChannelListeners.flushingChannelListener(null, new ChannelExceptionHandler() {
                                        @Override
                                        public void handleException(StreamSinkChannel channel, IOException exception) {
                                            IoUtils.safeClose(clientConnection);
                                            exchangeListener.taskFailed();
                                        }
                                    }));
                                    result.getRequestChannel().resumeWrites();
                                }
                            } catch (IOException e) {
                                IoUtils.safeClose(clientConnection);
                                exchangeListener.taskFailed();
                            }
                        }

                        @Override
                        public void failed(IOException e) {
                            exchangeListener.taskFailed();
                            IoUtils.safeClose(clientConnection);
                        }
                    });
                }

                @Override
                public void failed(IOException e) {
                    exchangeListener.taskFailed();
                }
            }, connection, thread, xnioSsl, bufferPool, options);

        }
    }

    static class RequestExchangeListener extends CancellableTask implements ClientCallback {

        private ClientExchange exchange;
        private final boolean closeConnection;
        private final NodeHealthChecker healthChecker;

        RequestExchangeListener(PingCallback callback, NodeHealthChecker healthChecker, boolean closeConnection) {
            super(callback);
            assert healthChecker != null;
            this.closeConnection = closeConnection;
            this.healthChecker = healthChecker;
        }

        @Override
        public void completed(final ClientExchange result) {
            if (isDone()) {
                IoUtils.safeClose(result.getConnection());
                return;
            }
            final ChannelListener listener = ChannelListeners.drainListener(Long.MAX_VALUE, new ChannelListener() {
                @Override
                public void handleEvent(StreamSourceChannel channel) {
                    try {
                        if (healthChecker.checkResponse(result.getResponse())) {
                            taskCompleted();
                        } else {
                            taskFailed();
                        }
                    } finally {
                        if (closeConnection) {
                            if (exchange != null) {
                                IoUtils.safeClose(exchange.getConnection());
                            }
                        }
                    }
                }
            }, new ChannelExceptionHandler() {
                @Override
                public void handleException(StreamSourceChannel channel, IOException exception) {
                    taskFailed();
                    if (exception != null) {
                        IoUtils.safeClose(exchange.getConnection());
                    }
                }
            });
            StreamSourceChannel responseChannel = result.getResponseChannel();
            responseChannel.getReadSetter().set(listener);
            responseChannel.resumeReads();
            listener.handleEvent(responseChannel);
        }

        @Override
        public void failed(IOException e) {
            taskFailed();
            if (exchange != null) {
                IoUtils.safeClose(exchange.getConnection());
            }
        }
    }

    enum State {
        WAITING, DONE, CANCELLED;
    }

    static class CancellableTask {

        private final PingCallback delegate;
        private volatile State state = State.WAITING;
        private volatile XnioExecutor.Key cancelKey;

        CancellableTask(PingCallback callback) {
            this.delegate = callback;
        }

        boolean isDone() {
            return state != State.WAITING;
        }

        void setCancelKey(XnioExecutor.Key cancelKey) {
            if (state == State.WAITING) {
                this.cancelKey = cancelKey;
            } else {
                cancelKey.remove();
            }
        }

        void taskCompleted() {
            if (state == State.WAITING) {
                state = State.DONE;
                if (cancelKey != null) {
                    cancelKey.remove();
                }
                delegate.completed();
            }
        }

        void taskFailed() {
            if (state == State.WAITING) {
                state = State.DONE;
                if (cancelKey != null) {
                    cancelKey.remove();
                }
                delegate.failed();
            }
        }

        void cancel() {
            if (state == State.WAITING) {
                state = State.CANCELLED;
                if (cancelKey != null) {
                    cancelKey.remove();
                }
                delegate.failed();
            }
        }

    }

    static void scheduleCancelTask(final XnioIoThread ioThread, final CancellableTask cancellable, final long timeout, final TimeUnit timeUnit ) {
        final XnioExecutor.Key key = WorkerUtils.executeAfter(ioThread, new Runnable() {
            @Override
            public void run() {
                cancellable.cancel();
            }
        }, timeout, timeUnit);
        cancellable.setCancelKey(key);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy