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

hprose.client.HproseTcpClient Maven / Gradle / Ivy

Go to download

Hprose is a High Performance Remote Object Service Engine. It is a modern, lightweight, cross-language, cross-platform, object-oriented, high performance, remote dynamic communication middleware. It is not only easy to use, but powerful. You just need a little time to learn, then you can use it to easily construct cross language cross platform distributed application system. Hprose supports many programming languages, for example: * AAuto Quicker * ActionScript * ASP * C++ * Dart * Delphi/Free Pascal * dotNET(C#, Visual Basic...) * Golang * Java * JavaScript * Node.js * Objective-C * Perl * PHP * Python * Ruby * ... Through Hprose, You can conveniently and efficiently intercommunicate between those programming languages. This project is the implementation of Hprose for Java.

There is a newer version: 2.0.38
Show newest version
/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * HproseTcpClient.java                                   *
 *                                                        *
 * hprose tcp client class for Java.                      *
 *                                                        *
 * LastModified: Jun 2, 2016                              *
 * Author: Ma Bingyao                   *
 *                                                        *
\**********************************************************/
package hprose.client;

import hprose.common.HproseException;
import hprose.io.HproseMode;
import hprose.net.Connection;
import hprose.net.ConnectionHandler;
import hprose.net.Connector;
import hprose.net.ReceiveCallback;
import hprose.net.TimeoutType;
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.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;


final class Request {
    public final ByteBuffer buffer;
    public final ReceiveCallback callback;
    public final int timeout;
    public Request(ByteBuffer buffer, ReceiveCallback callback, int timeout) {
        this.buffer = buffer;
        this.callback = callback;
        this.timeout = timeout;
    }
}

final class Response {
    public final ReceiveCallback callback;
    public final long createTime;
    public final int timeout;
    public Response(ReceiveCallback callback, int timeout) {
        this.callback = callback;
        this.createTime = System.currentTimeMillis();
        this.timeout = timeout;
    }
}

abstract class SocketTransporter extends Thread implements ConnectionHandler {
    protected final static class ConnectorHolder {
        final static Connector connector;
        static {
            Connector temp = null;
            try {
                temp = new Connector(HproseTcpClient.getReactorThreads());
            }
            catch (IOException e) {}
            finally {
                connector = temp;
                connector.start();
            }
            Threads.registerShutdownHandler(new Runnable() {
                public void run() {
                    if (connector != null) {
                        connector.close();
                    }
                }
            });
        }
    }
    protected final HproseTcpClient client;
    protected final BlockingQueue idleConnections = new LinkedBlockingQueue();
    protected final BlockingQueue requests = new LinkedBlockingQueue();
    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();
    }

    @Override
    public void run() {
        try {
            while (!isInterrupted()) {
                Request request;
                if (requests.isEmpty()) {
                    request = requests.take();
                    requests.offer(request);
                }
                if (idleConnections.isEmpty()) {
                    if (geRealPoolSize() < client.getMaxPoolSize()) {
                        try {
                            ConnectorHolder.connector.create(client.uri, this, client.isKeepAlive(), client.isNoDelay());
                        }
                        catch (IOException ex) {
                            while ((request = requests.poll()) != null) {
                                request.callback.handler(null, ex);
                            }
                        }
                    }
                }
                Connection conn = idleConnections.poll(client.getConnectTimeout(), TimeUnit.MILLISECONDS);
                if (conn != null) {
                    request = requests.poll();
                    if (request == null) {
                        request = requests.take();
                    }
                    send(conn, request);
                }
            }
        }
        catch (InterruptedException e) {}
    }

    protected abstract int geRealPoolSize();

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

    public final synchronized void send(ByteBuffer buffer, ReceiveCallback callback, int timeout) {
        requests.offer(new Request(buffer, callback, timeout));
    }

    protected void close(Map responses) {
        interrupt();
        while (!responses.isEmpty()) {
            Iterator it = responses.keySet().iterator();
            while (it.hasNext()) {
                Connection conn = it.next();
                conn.close();
            }
        }
        while (!requests.isEmpty()) {
            requests.poll().callback.handler(null, new ClosedChannelException());
        }
    }

    public final void onClose(Connection conn) {
        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>();
    private final Timer timer = new Timer(new Runnable() {
        public void run() {
            long currentTime = System.currentTimeMillis();
            Iterator>> iterator = responses.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry> entry = iterator.next();
                Connection conn = entry.getKey();
                Map res = entry.getValue();
                Iterator> it = res.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry e = it.next();
                    Response response = e.getValue();
                    if ((currentTime - response.createTime) >=  response.timeout) {
                        it.remove();
                        response.callback.handler(null, new TimeoutException("timeout"));
                    }
                }
                if (res.isEmpty() && conn.isConnected()) {
                    recycle(conn);
                }
            }
        }
    });

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

    private void init() {
        int timeout = Math.min(client.getTimeout(), client.getConnectTimeout());
        timeout =  Math.min(timeout, client.getReadTimeout());
        timeout =  Math.min(timeout, client.getWriteTimeout());
        timeout = Math.max(timeout, 1000);
        timer.setInterval((timeout + 1) >> 1);
        start();
    }

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

    protected final void send(Connection conn, Request request) {
        Map res = responses.get(conn);
        if (res != null) {
            if (res.size() < 10) {
                int id = nextId.incrementAndGet() & 0x7fffffff;
                res.put(id, new Response(request.callback, request.timeout));
                conn.send(request.buffer, id);
            }
            else {
                idleConnections.offer(conn);
                requests.offer(request);
            }
        }
    }

    protected final int geRealPoolSize() {
        return responses.size();
    }

    @Override
    @SuppressWarnings("unchecked")
    public final void close() {
        timer.clear();
        close((Map)(Object)responses);
    }

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

    public void onConnected(Connection 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.callback.handler(null, 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.callback.handler(null, new TimeoutException(type.toString()));
                }
            }
        }
    }

    public final void onReceived(Connection conn, ByteBuffer data, Integer id) {
        Map res = responses.get(conn);
        if (res != null) {
            Response response = res.remove(id);
            if (response != null) {
                if (data.position() != 0) {
                    data.flip();
                }
                response.callback.handler(data, null);
            }
            if (res.isEmpty()) {
                recycle(conn);
            }
        }
    }

    public final void onSended(Connection conn, Integer id) {
        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.callback.handler(null, e);
            }
        }
    }
}

final class HalfDuplexSocketTransporter extends SocketTransporter {
    private final static Response nullResponse = new Response(null, 0);
    private final Map responses = new ConcurrentHashMap();
    private final Timer timer = new Timer(new Runnable() {
        public void run() {
            long currentTime = System.currentTimeMillis();
            Iterator> it = responses.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry entry = it.next();
                Connection conn = entry.getKey();
                Response response = entry.getValue();
                if ((currentTime - response.createTime) >= response.timeout) {
                    it.remove();
                    response.callback.handler(null, new TimeoutException("timeout"));
                    conn.close();
                }
            }
        }
    });

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

    private void init() {
        int timeout = Math.min(client.getTimeout(), client.getConnectTimeout());
        timeout =  Math.min(timeout, client.getReadTimeout());
        timeout =  Math.min(timeout, client.getWriteTimeout());
        timeout = Math.max(timeout, 1000);
        timer.setInterval((timeout + 1) >> 1);
        start();
    }

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

    protected final void send(Connection conn, Request request) {
        responses.put(conn, new Response(request.callback, request.timeout));
        conn.send(request.buffer, null);
    }

    protected final int geRealPoolSize() {
        return responses.size();
    }

    @Override
    @SuppressWarnings("unchecked")
    public final void close() {
        timer.clear();
        close((Map)(Object)responses);
    }

    public void onConnect(Connection conn) {
        responses.put(conn, nullResponse);
    }

    public void onConnected(Connection 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.callback.handler(null, new TimeoutException("connect timeout"));
            }
        }
        else if (TimeoutType.IDLE_TIMEOUT != type) {
            Response response = responses.put(conn, nullResponse);
            if (response != null && response != nullResponse) {
                response.callback.handler(null, new TimeoutException(type.toString()));
            }
        }
        conn.close();
    }

    public final void onReceived(Connection conn, ByteBuffer data, Integer id) {
        Response response = responses.put(conn, nullResponse);
        recycle(conn);
        if (response != null && response != nullResponse) {
            if (data.position() != 0) {
                data.flip();
            }
            response.callback.handler(data, null);
        }
    }

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

    public final void onError(Connection conn, Exception e) {
        Response response = responses.remove(conn);
        if (response != null && response != nullResponse) {
            response.callback.handler(null, 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 = 2;
    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().toLowerCase();
        if (!scheme.equals("tcp") &&
            !scheme.equals("tcp4") &&
            !scheme.equals("tcp6")) {
            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().toLowerCase();
            if (!scheme.equals("tcp") &&
                !scheme.equals("tcp4") &&
                !scheme.equals("tcp6")) {
                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 synchronized 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;
    }

    @Override
    protected final ByteBuffer sendAndReceive(ByteBuffer request, int timeout) throws Throwable {
        final Result result = new Result();
        final Semaphore sem = new Semaphore(0);
        sendAndReceive(request, new ReceiveCallback() {
            public void handler(ByteBuffer buffer, Throwable e) {
                result.buffer = buffer;
                result.e = e;
                sem.release();
            }
        }, timeout);
        try {
            sem.acquire();
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        if (result.e == null) {
            return result.buffer;
        }
        throw result.e;
    }

    @Override
    protected final void sendAndReceive(ByteBuffer request, ReceiveCallback callback, int timeout) {
        if (fullDuplex) {
            fdTrans.send(request, callback, timeout);
        }
        else {
            hdTrans.send(request, callback, timeout);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy