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

hprose.client.HproseTcpClient Maven / Gradle / Ivy

/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * HproseTcpClient.java                                   *
 *                                                        *
 * hprose tcp client class for Java.                      *
 *                                                        *
 * LastModified: Feb 5, 2018                              *
 * Author: Ma Bingyao                   *
 *                                                        *
\**********************************************************/
package hprose.client;

import hprose.common.HproseException;
import hprose.common.InvokeSettings;
import hprose.io.HproseMode;
import hprose.net.Connection;
import hprose.net.ConnectionHandler;
import hprose.net.Connector;
import hprose.net.TimeoutType;
import hprose.util.concurrent.Promise;
import hprose.util.concurrent.Threads;
import hprose.util.concurrent.Timer;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

final class Request {
    public final ByteBuffer buffer;
    public final Promise result = new Promise();
    public final int timeout;
    public Request(ByteBuffer buffer, int timeout) {
        this.buffer = buffer;
        this.timeout = timeout;
    }
}

final class Response {
    public final Promise result;
    public final Timer timer;
    public Response(Promise result, Timer timer) {
        this.result = result;
        this.timer = timer;
    }
}

abstract class SocketTransporter implements ConnectionHandler {
    protected final static class ConnectorHolder {
        private static volatile Connector connector;
        private static void init() {
            Connector temp = null;
            try {
                temp = new Connector(HproseTcpClient.getReactorThreads());
            }
            catch (IOException e) {}
            finally {
                connector = temp;
            }
        }
        static {
            init();
            Threads.registerShutdownHandler(new Runnable() {
                public void run() {
                    Connector temp = connector;
                    init();
                    if (temp != null) {
                        temp.close();
                    }
                }
            });
        }
        public static final void create(String uri, ConnectionHandler handler, boolean keepAlive, boolean noDelay) throws IOException {
            if (!connector.isAlive()) {
                try {
                    connector.start();
                }
                catch (IllegalThreadStateException ignore) {}
            }
            connector.create(uri, handler, keepAlive, noDelay);
        }
    }

    protected final HproseTcpClient client;
    protected final LinkedList idleConnections = new LinkedList();
    protected final ConcurrentLinkedQueue requests = new ConcurrentLinkedQueue();
    protected final AtomicInteger size = new AtomicInteger(0);

    public SocketTransporter(HproseTcpClient client) {
        super();
        this.client = client;
    }

    public final int getReadTimeout() {
        return client.getReadTimeout();
    }
    public final int getWriteTimeout() {
        return client.getWriteTimeout();
    }
    public final int getConnectTimeout() {
        return client.getConnectTimeout();
    }

    protected final void create(Request request) {
        if (size.get() < client.getMaxPoolSize()) {
            try {
                ConnectorHolder.create(client.uri, this, client.isKeepAlive(), client.isNoDelay());
            }
            catch (IOException ex) {
                request.result.reject(ex);
//                while ((request = requests.poll()) != null) {
//                    request.result.reject(ex);
//                }
                return;
            }
        }
        requests.offer(request);
    }

    protected abstract Connection fetch(Request request);

    protected abstract void send(Connection conn, Request request);

    public final Promise send(ByteBuffer buffer, int timeout) {
        Request request = new Request(buffer, timeout);
        Connection conn = fetch(request);
        if (conn != null) {
            send(conn, request);
        }
        return request.result;
    }

    protected void close(Set conns) {
        while (!conns.isEmpty()) {
            for (Connection conn: conns) {
                conn.close();
            }
        }
        while (!requests.isEmpty()) {
            requests.poll().result.reject(new ClosedChannelException());
        }
    }

    public final void onClose(Connection conn) {
        size.decrementAndGet();
        synchronized (idleConnections) {
            idleConnections.remove(conn);
        }
        onError(conn, new ClosedChannelException());
    }

    public abstract void close();

}

final class FullDuplexSocketTransporter extends SocketTransporter {
    private final static AtomicInteger nextId = new AtomicInteger(0);
    private final Map> responses = new ConcurrentHashMap>();

    public FullDuplexSocketTransporter(HproseTcpClient client) {
        super(client);
    }

    protected final Connection fetch(Request request) {
        Connection conn;
        synchronized (idleConnections) {
            do {
                conn = idleConnections.poll();
                if (conn != null && conn.isConnected()) {
                    if (responses.get(conn).isEmpty()) {
                        conn.clearTimeout();
                    }
                    else {
                        conn = null;
                    }
                }
            } while (conn != null && !conn.isConnected());
            if (conn == null) create(request);
        }
        return conn;
    }

    private void recycle(Connection conn) {
        conn.setTimeout(client.getIdleTimeout(), TimeoutType.IDLE_TIMEOUT);
    }

    private Promise clean(Connection conn, int id) {
        Map res = responses.get(conn);
        Promise result = null;
        if (res != null) {
            Response response = res.remove(id);
            if (response != null) {
                response.timer.clear();
                result = response.result;
            }
            sendNext(conn, res);
        }
        return result;
    }

    private void sendNext(Connection conn, Map res) {
        if (res.size() < 10) {
            Request request = requests.poll();
            if (request != null) {
                send(conn, request);
            }
            else {
                synchronized (idleConnections) {
                    if (!idleConnections.contains(conn)) {
                        idleConnections.offer(conn);
                    }
                }
            }
        }
    }

    protected final void send(final Connection conn, Request request) {
        final Map res = responses.get(conn);
        if (res != null) {
            final int id = nextId.incrementAndGet() & 0x7fffffff;
            Timer timer = new Timer(new Runnable() {
                public void run() {
                    Promise result = clean(conn, id);
                    if (res.isEmpty()) {
                        recycle(conn);
                    }
                    if (result != null) {
                        result.reject(new TimeoutException("timeout"));
                    }
                }
            });
            timer.setTimeout(request.timeout);
            res.put(id, new Response(request.result, timer));
            conn.send(request.buffer, id);
            sendNext(conn, res);
        }
    }

    @Override
    public final void close() {
        close(responses.keySet());
    }

    public final void onConnect(Connection conn) {
        size.incrementAndGet();
        responses.put(conn, new ConcurrentHashMap());
    }

    public final void onConnected(Connection conn) {
        synchronized (idleConnections) {
            Request request = requests.poll();
            if (request != null) {
                send(conn, request);
            }
            else {
                if (!idleConnections.contains(conn)) {
                    idleConnections.offer(conn);
                }
                recycle(conn);
            }
        }
    }

    public final void onTimeout(Connection conn, TimeoutType type) {
        if (TimeoutType.CONNECT_TIMEOUT == type) {
            responses.remove(conn);
            Request request;
            while ((request = requests.poll()) != null) {
                request.result.reject(new TimeoutException("connect timeout"));
            }
        }
        else if (TimeoutType.IDLE_TIMEOUT != type) {
            Map res = responses.get(conn);
            if (res != null) {
                Iterator> it = res.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = it.next();
                    it.remove();
                    Response response = entry.getValue();
                    response.timer.clear();
                    response.result.reject(new TimeoutException(type.toString()));
                }
            }
        }
    }

    public final void onReceived(Connection conn, ByteBuffer data, Integer id) {
        Promise result = clean(conn, id);
        if (result != null) {
            if (data.position() != 0) {
                data.flip();
            }
            result.resolve(data);
        }
        else {
            recycle(conn);
        }
    }

    public final void onSended(Connection conn, ByteBuffer data, Integer id) {
        synchronized (idleConnections) {
            if (!idleConnections.contains(conn)) {
                idleConnections.offer(conn);
            }
        }
    }

    public final void onError(Connection conn, Exception e) {
        Map res = responses.remove(conn);
        if (res != null) {
            Iterator> it = res.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry entry = it.next();
                it.remove();
                Response response = entry.getValue();
                response.timer.clear();
                response.result.reject(e);
            }
        }
    }
}

final class HalfDuplexSocketTransporter extends SocketTransporter {
    private final Map responses = new ConcurrentHashMap();

    public HalfDuplexSocketTransporter(HproseTcpClient client) {
        super(client);
    }

    protected final Connection fetch(Request request) {
        Connection conn;
        synchronized (idleConnections) {
            do {
                conn = idleConnections.poll();
            } while (conn != null && !conn.isConnected());
            if (conn != null) {
                conn.clearTimeout();
            }
            else {
                create(request);
            }
        }
        return conn;
    }

    private void recycle(Connection conn) {
        synchronized (idleConnections) {
            if (!idleConnections.contains(conn)) {
                idleConnections.offer(conn);
                conn.setTimeout(client.getIdleTimeout(), TimeoutType.IDLE_TIMEOUT);
            }
        }
    }

    private Promise clean(Connection conn) {
        Response response = responses.remove(conn);
        if (response != null) {
            response.timer.clear();
            return response.result;
        }
        return null;
    }

    private void sendNext(Connection conn) {
        Request request = requests.poll();
        if (request != null) {
            send(conn, request);
        }
        else {
            recycle(conn);
        }
    }

    protected final void send(final Connection conn, Request request) {
        Timer timer = new Timer(new Runnable() {
            public void run() {
                Promise result = clean(conn);
                conn.close();
                if (result != null) {
                    result.reject(new TimeoutException("timeout"));
                }
            }
        });
        timer.setTimeout(request.timeout);
        responses.put(conn, new Response(request.result, timer));
        conn.send(request.buffer, null);
    }

    @Override
    public final void close() {
        close(responses.keySet());
    }

    public final void onConnect(Connection conn) {
        size.incrementAndGet();
    }

    public final void onConnected(Connection conn) {
        sendNext(conn);
    }

    public final void onTimeout(Connection conn, TimeoutType type) {
        if (TimeoutType.CONNECT_TIMEOUT == type) {
            responses.remove(conn);
            Request request;
            while ((request = requests.poll()) != null) {
                request.result.reject(new TimeoutException("connect timeout"));
            }
        }
        else if (TimeoutType.IDLE_TIMEOUT != type) {
            Response response = responses.remove(conn);
            if (response != null) {
                response.timer.clear();
                response.result.reject(new TimeoutException(type.toString()));
            }
        }
        conn.close();
    }

    public final void onReceived(Connection conn, ByteBuffer data, Integer id) {
        Promise result = clean(conn);
        if (result != null) {
            if (data.position() != 0) {
                data.flip();
            }
            result.resolve(data);
        }
        sendNext(conn);
    }

    public final void onSended(Connection conn, ByteBuffer data, Integer id) {}

    public final void onError(Connection conn, Exception e) {
        Response response = responses.remove(conn);
        if (response != null) {
            response.timer.clear();
            response.result.reject(e);
        }
    }
}

final class Result {
    public volatile ByteBuffer buffer;
    public volatile Throwable e;
}

public class HproseTcpClient extends HproseClient {
    private static int reactorThreads = 2;

    public static int getReactorThreads() {
        return reactorThreads;
    }

    public static void setReactorThreads(int aReactorThreads) {
        reactorThreads = aReactorThreads;
    }

    private volatile boolean fullDuplex = false;
    private volatile boolean noDelay = false;
    private volatile int maxPoolSize = 4;
    private volatile int idleTimeout = 30000;
    private volatile int readTimeout = 30000;
    private volatile int writeTimeout = 30000;
    private volatile int connectTimeout = 30000;
    private volatile boolean keepAlive = true;
    private final SocketTransporter fdTrans = new FullDuplexSocketTransporter(this);
    private final SocketTransporter hdTrans = new HalfDuplexSocketTransporter(this);

    public HproseTcpClient() {
        super();
    }

    public HproseTcpClient(String uri) {
        super(uri);
    }

    public HproseTcpClient(HproseMode mode) {
        super(mode);
    }

    public HproseTcpClient(String uri, HproseMode mode) {
        super(uri, mode);
    }

    public HproseTcpClient(String[] uris) {
        super(uris);
    }

    public HproseTcpClient(String[] uris, HproseMode mode) {
        super(uris, mode);
    }

    public static HproseClient create(String uri, HproseMode mode) throws IOException, URISyntaxException {
        String scheme = (new URI(uri)).getScheme();
        if (!"tcp".equalsIgnoreCase(scheme) &&
            !"tcp4".equalsIgnoreCase(scheme) &&
            !"tcp6".equalsIgnoreCase(scheme)) {
            throw new HproseException("This client doesn't support " + scheme + " scheme.");
        }
        return new HproseTcpClient(uri, mode);
    }

    public static HproseClient create(String[] uris, HproseMode mode) throws IOException, URISyntaxException {
        for (int i = 0, n = uris.length; i < n; ++i) {
            String scheme = (new URI(uris[i])).getScheme();
            if (!"tcp".equalsIgnoreCase(scheme) &&
                !"tcp4".equalsIgnoreCase(scheme) &&
                !"tcp6".equalsIgnoreCase(scheme)) {
                throw new HproseException("This client doesn't support " + scheme + " scheme.");
            }
        }
        return new HproseTcpClient(uris, mode);
    }

    @Override
    public final void close() {
        fdTrans.close();
        hdTrans.close();
        super.close();
    }

    public final boolean isFullDuplex() {
        return fullDuplex;
    }

    public final void setFullDuplex(boolean fullDuplex) {
        this.fullDuplex = fullDuplex;
    }

    public final boolean isNoDelay() {
        return noDelay;
    }

    public final void setNoDelay(boolean noDelay) {
        this.noDelay = noDelay;
    }

    public final int getMaxPoolSize() {
        return maxPoolSize;
    }

    public final void setMaxPoolSize(int maxPoolSize) {
        if (maxPoolSize < 1) throw new IllegalArgumentException("maxPoolSize must be great than 0");
        this.maxPoolSize = maxPoolSize;
    }

    public final int getIdleTimeout() {
        return idleTimeout;
    }

    public final void setIdleTimeout(int idleTimeout) {
        if (idleTimeout < 0) throw new IllegalArgumentException("idleTimeout must be great than -1");
        this.idleTimeout = idleTimeout;
    }

    public final int getReadTimeout() {
        return readTimeout;
    }

    public final void setReadTimeout(int readTimeout) {
        if (readTimeout < 1) throw new IllegalArgumentException("readTimeout must be great than 0");
        this.readTimeout = readTimeout;
    }

    public final int getWriteTimeout() {
        return writeTimeout;
    }

    public final void setWriteTimeout(int writeTimeout) {
        if (writeTimeout < 1) throw new IllegalArgumentException("writeTimeout must be great than 0");
        this.writeTimeout = writeTimeout;
    }

    public final int getConnectTimeout() {
        return connectTimeout;
    }

    public final void setConnectTimeout(int connectTimeout) {
        if (connectTimeout < 1) throw new IllegalArgumentException("connectTimeout must be great than 0");
        this.connectTimeout = connectTimeout;
    }

    public final boolean isKeepAlive() {
        return keepAlive;
    }

    public final void setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    protected Promise sendAndReceive(final ByteBuffer request, ClientContext context) {
        final InvokeSettings settings = context.getSettings();
        if (fullDuplex) {
            return fdTrans.send(request, settings.getTimeout());
        }
        else {
            return hdTrans.send(request, settings.getTimeout());
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy