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

reactor.net.zmq.tcp.ZeroMQ Maven / Gradle / Ivy

The newest version!
package reactor.net.zmq.tcp;

import com.gs.collections.api.list.MutableList;
import com.gs.collections.impl.block.function.checked.CheckedFunction0;
import com.gs.collections.impl.block.predicate.checked.CheckedPredicate;
import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.list.mutable.SynchronizedMutableList;
import com.gs.collections.impl.map.mutable.SynchronizedMutableMap;
import com.gs.collections.impl.map.mutable.UnifiedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zeromq.ZContext;
import org.zeromq.ZMQ;
import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.core.composable.Deferred;
import reactor.core.composable.Promise;
import reactor.core.composable.spec.Promises;
import reactor.core.spec.Reactors;
import reactor.event.dispatch.Dispatcher;
import reactor.io.Buffer;
import reactor.io.encoding.Codec;
import reactor.io.encoding.StandardCodecs;
import reactor.net.NetChannel;
import reactor.net.tcp.TcpClient;
import reactor.net.tcp.TcpServer;
import reactor.net.tcp.spec.TcpClientSpec;
import reactor.net.tcp.spec.TcpServerSpec;
import reactor.net.zmq.ZeroMQClientSocketOptions;
import reactor.net.zmq.ZeroMQServerSocketOptions;
import reactor.support.NamedDaemonThreadFactory;
import reactor.util.Assert;

import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author Jon Brisbin
 */
public class ZeroMQ {

	private static final SynchronizedMutableMap SOCKET_TYPES = SynchronizedMutableMap.of(UnifiedMap.newMap());

	private final Logger                       log     = LoggerFactory.getLogger(getClass());
	private final MutableList> clients = SynchronizedMutableList.of(FastList.>newList());
	private final MutableList> servers = SynchronizedMutableList.of(FastList.>newList());

	private final ExecutorService threadPool = Executors.newCachedThreadPool(new NamedDaemonThreadFactory("zmq"));

	private final Environment env;
	private final Dispatcher  dispatcher;
	private final Reactor     reactor;
	private final ZContext    zmqCtx;

	@SuppressWarnings("unchecked")
	private volatile Codec codec    = (Codec) StandardCodecs.PASS_THROUGH_CODEC;
	private volatile boolean             shutdown = false;

	public ZeroMQ(Environment env) {
		this(env, env.getDefaultDispatcher());
	}

	public ZeroMQ(Environment env, String dispatcher) {
		this(env, env.getDispatcher(dispatcher));
	}

	public ZeroMQ(Environment env, Dispatcher dispatcher) {
		this.env = env;
		this.dispatcher = dispatcher;
		this.reactor = Reactors.reactor(env, dispatcher);
		this.zmqCtx = new ZContext();
		this.zmqCtx.setLinger(100);
	}

	public static String findSocketTypeName(final int socketType) {
		return SOCKET_TYPES.getIfAbsentPut(socketType, new CheckedFunction0() {
			@Override
			public String safeValue() throws Exception {
				for (Field f : ZMQ.class.getDeclaredFields()) {
					if (int.class.isAssignableFrom(f.getType())) {
						f.setAccessible(true);
						try {
							int val = f.getInt(null);
							if (socketType == val) {
								return f.getName();
							}
						} catch (IllegalAccessException e) {
						}
					}
				}
				return "";
			}
		});
	}

	public ZeroMQ codec(Codec codec) {
		this.codec = codec;
		return this;
	}

	public Promise> dealer(String addrs) {
		return createClient(addrs, ZMQ.DEALER);
	}

	public Promise> push(String addrs) {
		return createClient(addrs, ZMQ.PUSH);
	}

	public Promise> pull(String addrs) {
		return createServer(addrs, ZMQ.PULL);
	}

	public Promise> request(String addrs) {
		return createClient(addrs, ZMQ.REQ);
	}

	public Promise> reply(String addrs) {
		return createServer(addrs, ZMQ.REP);
	}

	public Promise> router(String addrs) {
		return createServer(addrs, ZMQ.ROUTER);
	}

	private Promise> createClient(String addrs, int socketType) {
		Assert.isTrue(!shutdown, "This ZeroMQ instance has been shut down");

		TcpClient client = new TcpClientSpec(ZeroMQTcpClient.class)
				.env(env).dispatcher(dispatcher).codec(codec)
				.options(new ZeroMQClientSocketOptions()
						         .context(zmqCtx)
						         .connectAddresses(addrs)
						         .socketType(socketType))
				.get();

		clients.add(client);

		return client.open();
	}

	public Promise> createServer(String addrs, int socketType) {
		Assert.isTrue(!shutdown, "This ZeroMQ instance has been shut down");

		Deferred, Promise>> d = Promises.defer(env, dispatcher);

		TcpServer server = new TcpServerSpec(ZeroMQTcpServer.class)
				.env(env).dispatcher(dispatcher).codec(codec)
				.options(new ZeroMQServerSocketOptions()
						         .context(zmqCtx)
						         .listenAddresses(addrs)
						         .socketType(socketType))
				.consume(d)
				.get();

		servers.add(server);

		server.start();

		return d.compose();
	}

	public void shutdown() {
		if (shutdown) {
			return;
		}
		shutdown = true;

		servers.removeIf(new CheckedPredicate>() {
			@Override
			public boolean safeAccept(TcpServer server) throws Exception {
				Assert.isTrue(server.shutdown().await(60, TimeUnit.SECONDS), "Server " + server + " not properly shut down");
				return true;
			}
		});
		clients.removeIf(new CheckedPredicate>() {
			@Override
			public boolean safeAccept(TcpClient client) throws Exception {
				Assert.isTrue(client.close().await(60, TimeUnit.SECONDS), "Client " + client + " not properly shut down");
				return true;
			}
		});

//		if (log.isDebugEnabled()) {
//			log.debug("Destroying {} on {}", zmqCtx, this);
//		}
//		zmqCtx.destroy();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy