pax_global_header00006660000000000000000000000064145577112630014525gustar00rootroot0000000000000052 comment=70639a469154589521f35f8f33df5f8a715b77da jeromq-0.6.0/000077500000000000000000000000001455771126300130255ustar00rootroot00000000000000jeromq-0.6.0/.circleci/000077500000000000000000000000001455771126300146605ustar00rootroot00000000000000jeromq-0.6.0/.circleci/config.yml000066400000000000000000000221531455771126300166530ustar00rootroot00000000000000version: 2.1 jobs: checkout: docker: - image: cimg/openjdk:21.0 steps: - checkout - restore_cache: key: jeromq-{{ checksum "pom.xml" }} - run: mkdir -p /home/circleci/.sonar/cache /home/circleci/.m2/repository - run: command: "mvn -B dependency:resolve dependency:resolve-plugins sonar:help jacoco:help checkstyle:help -Psonar,versions,android -s .circleci/settings.xml" - persist_to_workspace: root: / paths: - home/circleci/.m2/repository - home/circleci/.sonar/cache - home/circleci/project build: docker: - image: cimg/openjdk:21.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: mvn -B clean compile -Pcheckstyle -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx2048m" - persist_to_workspace: root: / paths: - home/circleci/.m2/repository - home/circleci/project/target testsj21: docker: - image: cimg/openjdk:21.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | mkdir -p /tmp/$CIRCLE_JOB mvn -B test -Pskip -Darg.line="-Xmx2048m" -Djava.io.tmpdir="/tmp/$CIRCLE_JOB" -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx1024m" - store_test_results: path: target/surefire-reports testsj17: docker: - image: cimg/openjdk:17.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | # If no symbolic name, it's a PR, will run sonar if [ -n "$(git symbolic-ref HEAD 2>/dev/null )" ] ; then SONAR="jacoco:report sonar:sonar -Psonar" echo "Doing sonar" else SONAR="" fi mkdir -p /tmp/$CIRCLE_JOB mvn -B test $SONAR -Pskip -Darg.line="-Xmx2048m" -Djava.io.tmpdir="/tmp/$CIRCLE_JOB" -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx1024m" - store_test_results: path: target/surefire-reports - persist_to_workspace: root: / paths: - home/circleci/.m2/repository - home/circleci/.sonar/cache - home/circleci/project testsj11: docker: - image: cimg/openjdk:11.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/.sonar /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | mkdir -p /tmp/$CIRCLE_JOB mvn -B test -Pskip -Darg.line="-Xmx2048m" -Djava.io.tmpdir="/tmp/$CIRCLE_JOB" -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx512m" - store_test_results: path: target/surefire-reports #don't persist_to_workspace, can't be done in parallel with testsj13 testsj8: docker: - image: cimg/openjdk:8.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/.sonar /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | mvn -B test -Pskip -Darg.line="-Xmx2048m" -Djava.io.tmpdir="/tmp/$CIRCLE_JOB" -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx512m" - store_test_results: path: target/surefire-reports #don't persist_to_workspace, can't be done in parallel with testsj13 testsandroid: docker: - image: cimg/openjdk:21.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/.sonar /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | mvn -B test -Pskip,android -Darg.line="-Xmx2048m" -s .circleci/settings.xml -Dmaven.test.skip=true environment: MAVEN_OPTS: "-Xmx2048m" publish: docker: - image: cimg/openjdk:21.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv -n /tmp/ws/home/circleci/.m2 /home/circleci/ mv -n /tmp/ws/home/circleci/.sonar /home/circleci/ mv -n /tmp/ws/home/circleci/project/* /home/circleci/project/ mv -n /tmp/ws/home/circleci/project/.??* /home/circleci/project/ - run: command: | if [ -n "$SECRING_GPG_ASC_BASE64" ] ; then echo "$SECRING_GPG_ASC_BASE64" | base64 --decode | gpg --batch --no-tty --import --yes GPGSIGN="-P gpgsign" fi # If no symbolic name, it's a PR, will not publish results # If not the official github, it's a fork, don't publish either if [ -n "$(git symbolic-ref HEAD 2>/dev/null )" -a "$CIRCLE_REPOSITORY_URL" == "git@github.com:zeromq/jeromq.git" ] ; then PUBLISH="deploy" else PUBLISH="install" fi echo "publishing jobs: $PUBLISH" mvn -B $PUBLISH -Pskip $GPGSIGN -Dmaven.test.skip=true -Dmaven.javadoc.skip=false -s .circleci/settings.xml environment: MAVEN_OPTS: "-Xmx2048m" - store_artifacts: path: /home/circleci/.m2/repository/org/zeromq/jeromq destination: / - persist_to_workspace: root: / paths: - home/circleci/.m2/repository - home/circleci/.sonar/cache savecache: docker: - image: cimg/openjdk:21.0 steps: - attach_workspace: at: /tmp/ws - run: command: | mv /tmp/ws/home/circleci/project/pom.xml /home/circleci/project/ mv /tmp/ws/home/circleci/.m2 /home/circleci/ mv /tmp/ws/home/circleci/.sonar /home/circleci/ - save_cache: paths: - /home/circleci/.m2/repository - /home/circleci/.sonar/cache key: jeromq-{{ checksum "pom.xml" }} workflows: version: 2.1 build_and_test: jobs: - checkout - build: requires: - checkout - testsj8: requires: - build - testsj11: requires: - build - testsj17: requires: - build - testsj21: requires: - build - testsandroid: requires: - build - publish: requires: - testsj8 - testsj11 - testsj17 - testsj21 - testsandroid - savecache: requires: - publish jeromq-0.6.0/.circleci/settings.xml000066400000000000000000000017061455771126300172460ustar00rootroot00000000000000 ossrh ${env.SONATYPE_USERNAME} ${env.SONATYPE_PASSWORD} sonar https://sonarcloud.io ${env.SONARCLOUD_KEY} ${env.SONARCLOUD_ORG} ${env.SONARCLOUD_LOGIN} skip true true true jeromq-0.6.0/.gitignore000066400000000000000000000001761455771126300150210ustar00rootroot00000000000000*.jar *.iml .project .classpath .settings .idea target maven-build.properties maven-build.xml build.xml .checkstyle .DS_Store jeromq-0.6.0/.java-version000066400000000000000000000000041455771126300154240ustar00rootroot000000000000001.8 jeromq-0.6.0/AUTHORS000066400000000000000000000031111455771126300140710ustar00rootroot00000000000000Contributors ============ AJ Lewis Alexej Lotz Andrew Thompson Asko Kauppi Barak Amar Ben Gray Bernd Melchers Bernd Prager Bob Beaty Brett Cameron Brian Buchanan Burak Arslan Carl Clemens Chia-liang Kao Chris Rempel Chris Wong Christian Gudrian Chuck Remes Conrad D. Steenberg Constantin Rack Dave Yarwood Dhammika Pathirana Dhruva Krishnamurthy Dirk O. Kaar Dongmin Yu Douglas Creager Erich Heine Erik Algell Erik Rigtorp Fabien Ninoles Frank Denis Frederic Delechamp George Neill Gerard Toonstra Ghislain Putois Gonzalo Diethelm Guido Goldstein Ian Barber Ilja Golshtein Isa Hekmatizadeh Ivo Danihelka Jacob Rideout Joe Thornber Jon Dyte Kamil Shakirov Lourens Naudé Marc Rossi Martin Hurton Martin Lucina Martin Pales Martin Sustrik Matus Hamorsky Max Wolf McClain Looney Michael Compton Mika Fischer Mikael Helbo Kjaer Mikko Koppanen Min Ragan-Kelley Neale Ferguson Nir Soffer Paul Betts Paul Colomiets Pavel Gushcha Pavol Malosek Perry Kundert Peter Bourgon Philip Kovacs Pieter Hintjens Piotr Trojanek Robert G. Jakabosky Sebastian Otaegui Steven McCoy Stuart Webster Tamara Kustarova Taras Shpot Tero Marttila Terry Wilson Thijs Terlouw Toralf Wittner Tore Halvorsen Trevor Bernard Vitaly Mayatskikh Credits ======= Aamir Mohammad Adrian von Bidder Aleksey Yeschenko Alessio Spadaro Alexander Majorov Anh Vu Bernd Schumacher Brian Granger Carsten Dinkelmann David Bahi Dirk Eddelbuettel Evgueny Khartchenko Frank Vanden Berghen Ian Barber John Apps Markus Fischer Matt Muggeridge Michael Santy Oleg Sevostyanov Paulo Henrique Silva Peter Busser Peter Lemenkov Robert Zhang Toralf Wittner Zed Shaw jeromq-0.6.0/CHANGELOG.md000066400000000000000000001105471455771126300146460ustar00rootroot00000000000000# Changelog ## v0.6.0 (2024-02-04) ### Added * [#964] https://github.com/zeromq/jeromq/pull/964 Exports all options default values as constant in ZMQ class. This will allow client libraries to use them. * [#963] (https://github.com/zeromq/jeromq/pull/963) Replaces maven-bundle-plugin with the more up-to-date bnd-maven-plugin and configures it to generate a JPMS module descriptor besides the OSGi descriptor. * [#949] (https://github.com/zeromq/jeromq/pull/949) Added a way to specify a custom monitor, that don’t need to communicate through a ZMQ socket. It allows to use monitor for logging without hitting hard on the CPU with poller overload. Improved in [#950] (https://github.com/zeromq/jeromq/pull/950) * [#942] (https://github.com/zeromq/jeromq/pull/942) `zmq.Ctx` can now be provided a thread factory, that can used to tweaks threads. The main purpose of this is to be able to bind thread to CPU using external libraries like https://github.com/OpenHFT/Java-Thread-Affinity. The idea was suggested by issue #910. * Also adding a check that prevent modifications of some settings once they have been used by `zmq.Ctx` initialisation. ### Changed * Many code smell removed using SonarLint. * [#941] (https://github.com/zeromq/jeromq/issues/940) Fix to issue [#940](https://github.com/zeromq/jeromq/issues/940) by setting `key=null on setKey, added test * [#939] (https://github.com/zeromq/jeromq/issues/939) Simplified Mailbox, always thread safe, so `MailboxSafe usages are removed and the class is marked at deprecated. * [#936] (https://github.com/zeromq/jeromq/pull/936) `org.zeromq.ZSocket` and `org.zeromq.ZMQ.Socket` can now read and write `zmq.Msg` directly. Updating `Metada` class to provide more data access and functions. `ZMetadata` is updated too. It internally now uses `ConcurrentHashMap` instead of the old `Properties class`. The `Properties` class ignore null values, `ConcurrentHashMap` reject them but ZMTP protocol allows empty values. So null values are transformed to empty strings. `Metada#set` is deprecated and replaced by `Metada#put`, to be more consistent with Map API. Lots of hostile final method declaration removed. ## v0.5.4 (2023-09-26) ### Changed * new GPG signature for artifact, id 9C925EE1. * [#935](https://github.com/zeromq/jeromq/pull/935): With org.zeromq.ZMQ.Event.recv(socket, DONTWAIT), if there is no more, event to receive, it throws an NPE although the javadoc says it should return null. Fixed. Also don’t resolve event value for ZMQ_EVENT_MONITOR_STOPPED, it’s a constant. * [#937] (https://github.com/zeromq/jeromq/pull/937): When doing PLAIN or CURVE authentication, the logic can be exchanged. The client is doing the bind, and the server doing the connect. That was not handled, a connect socket was not expected to reuse the session, and so found an already configured zapPipe. It’s now handled. Some error message was not returning any message, the specification says that an empty error should be returned instead. Big rewrite of the tests for mechanisms, the big test function is split and more code is shared. ## v0.5.3 (2022-12-03) ### Added * [#921](https://github.com/zeromq/jeromq/pull/921): Add peer support disconnect * [#906](https://github.com/zeromq/jeromq/pull/906): Fix issue where socket identity was failing with overflow when identity was bigger than 127 * [#903](https://github.com/zeromq/jeromq/pull/903): Make JeroMQ compatiable with Android API Level 19 * [#902](https://github.com/zeromq/jeromq/pull/902): Add tests and build on Java 17. Helping dependecies resolution by activating more profiles and don't try to publish on forks * [#775](https://github.com/zeromq/jeromq/pull/775): ZMQ_HEARTBEAT is not useful without sending an hello message.To solve that, the majordomo worker still has to implement heartbeat. With this new option, whenever the connection drops and reconnects the hello message will be sent, greatly simplify the majordomo protocol, as now READY and HEARTBEAT can be handled by zeromq. * [#783](https://github.com/zeromq/jeromq/pull/777): Jeromq is not thread-safe, so port CLIENT and SERVER sockets from libzmq, which are thread-safe sockets. * [#808](https://github.com/zeromq/jeromq/pull/808): Add Client/Server support to ZFrame. * [#837](https://github.com/zeromq/jeromq/pull/837): Radio-Dish implementation. * [#880](https://github.com/zeromq/jeromq/pull/880): Port of https://github.com/zeromq/libzmq/pull/3871, router can handle peer disconnected. * [#898](https://github.com/zeromq/jeromq/pull/898): Adding critical and notification exceptions handlers in zmq.poll.Poller. ### Changed * [#919](https://github.com/zeromq/jeromq/pull/919): Fix deadlock issue on socket close * [#906](https://github.com/zeromq/jeromq/pull/906): Fix issue where socket identity was failing with overflow when identity was bigger than 127 * [#903](https://github.com/zeromq/jeromq/pull/903): Make JeroMQ compatiable with Android API Level 19 * [#777](https://github.com/zeromq/jeromq/pull/777): ZMQ.Socket now remember the ZContext that created it and remove from it when closed. * Many improvement to error handling, with more error messages. * [#772](https://github.com/zeromq/jeromq/pull/772): Fix ZMQ_REQ_CORRELATE. * [#797](https://github.com/zeromq/jeromq/pull/797): A new ZBeacon implementation. * [#814](https://github.com/zeromq/jeromq/pull/814): IPC protocol now comply to java.net.preferIPv4Stack or java.net.preferIPv6Addresses for the choice of the TCP stack to use. * [#825](https://github.com/zeromq/jeromq/pull/825): Improved monitor, with added events in some mechanisms. ## v0.5.2 (2020-01-31) ### Added * [#715](https://github.com/zeromq/jeromq/pull/715): Added a ZCert constructor that takes a Writer as an argument, in order to support writing to the Writer instead of to a file. * [#716](https://github.com/zeromq/jeromq/pull/716): Added a ZTicket API, as well as a ZTicker API, which combines ZTimer and ZTicket. * [#724](https://github.com/zeromq/jeromq/pull/724): Added support for the XPUB options `ZMQ_XPUB_MANUAL` and `ZMQ_XPUB_VERBOSER`. * [#727](https://github.com/zeromq/jeromq/pull/727): Added a ZSocket constructor that takes a SocketType enum value as an argument. * [#747](https://github.com/zeromq/jeromq/pull/747): Improvements to ZBeacon: * Added `startClient` and `startServer` methods, to support restarting the client or server individually. * You can now specify the interface address when constructing a BroadcastClient. * [#755](https://github.com/zeromq/jeromq/pull/755): Added ZCert constructors that take (mandatory) public and (optional) secret keys as arguments. ### Changed * Fixes for Android compatibility: * [#710](https://github.com/zeromq/jeromq/pull/710): Use traditional loops instead of streams. * [#717](https://github.com/zeromq/jeromq/pull/717): Don't use `Map.computeIfAbsent`. * [#736](https://github.com/zeromq/jeromq/pull/736): Use java.util.Iterator instead of lambdas. * [#752](https://github.com/zeromq/jeromq/pull/752): Various fixes discovered by creating an Android project within the JeroMQ repo for testing purposes. * [#720](https://github.com/zeromq/jeromq/pull/720): Removed a println debug statement in `Poller.rebuildSelector`. * [#733](https://github.com/zeromq/jeromq/pull/733): Fixed a bug introduced in JeroMQ 0.5.1 where `ZPoller.poll` was returning -1 instead of 1. * [#735](https://github.com/zeromq/jeromq/pull/735): Fixed bugs related to the handling of bytes in the Msg class. * [#759](https://github.com/zeromq/jeromq/pull/759): Fixed an IndexOutOfBoundsException that occurs when the number of subscriptions exceeds the HWM. ## v0.5.1 (2019-04-03) ### Added * [#677](https://github.com/zeromq/jeromq/pull/677): ZPoller now supports registering multiple event handlers on a single socket or channel. * [#685](https://github.com/zeromq/jeromq/pull/685), [#687](https://github.com/zeromq/jeromq/pull/687): ZMQ.Socket has new methods that encode and decode messages based on a picture pattern which is compatible to ZProto: `sendPicture`, `recvPicture`, `sendBinaryPicture` and `recvBinaryPicture`. * [#692](https://github.com/zeromq/jeromq/pull/692): Added an overload of the ZBeacon that has an additional `serverAddress` option so that the broadcast address can be specified. The default value is still `255.255.255.255`. * [#694](https://github.com/zeromq/jeromq/pull/694): Added a draft ZNeedle helper class for serialization and deserialization within a frame. * [#697](https://github.com/zeromq/jeromq/pull/697): Added encoding/decoding of the `COMMAND` flag when using CURVE encryption. * [#698](https://github.com/zeromq/jeromq/pull/698): Added a `Msg.putShortString` method. ### Changed * [#671](https://github.com/zeromq/jeromq/pull/671), [#672](https://github.com/zeromq/jeromq/pull/672): In the internal `zmq.io.StreamEngine` class, a `ZError.InstantiationException` is now thrown when a decoder or encoder cannot be instantiated. Previously, a stacktrace would be printed and `null` would be returned instead of a decoder/encoder instance. * [#673](https://github.com/zeromq/jeromq/pull/673): `zmq.Mailbox.recv` now handles `EINTR` by returning `null`. This can happen, for example, if the channel is closed. * [#679](https://github.com/zeromq/jeromq/pull/679): Fixed a file descriptor leak when opening a TCP connection. * [#680](https://github.com/zeromq/jeromq/pull/680): Various improvements to support for IPv6 and name resolution. IPv6 is now enabled if the properties `java.net.preferIPv4Stack=false` or `java.net.preferIPv6Addresses=true` are set. * [#684](https://github.com/zeromq/jeromq/pull/684): Fixed a bug where `zmq.Msg.getBytes` was writing to an internal buffer instead of the given buffer. * [#688](https://github.com/zeromq/jeromq/pull/688): Javadoc fixes. * [#691](https://github.com/zeromq/jeromq/pull/691): Fixed a bug where timers would accumulate in the PollerBase when failed connections were retried, causing a memory leak. * [#693](https://github.com/zeromq/jeromq/pull/693): Fixed a Java 8-related compilation error. * [#702](https://github.com/zeromq/jeromq/pull/702): Removed all usage of `java.util.function`, `java.util.stream`, `java.util.Objects` and `java.util.Optional`, which are known to cause problems for some versions of Android. Replaced their usage with internal implementations. ## v0.5.0 (2019-02-18) ### Added * [#539](https://github.com/zeromq/jeromq/pull/539), [#552](https://github.com/zeromq/jeromq/pull/552), [#573](https://github.com/zeromq/jeromq/pull/573): Implemented heartbeating between sockets as specified in [https://rfc.zeromq.org/spec:37/ZMTP](https://rfc.zeromq.org/spec:37/ZMTP). * [#556](https://github.com/zeromq/jeromq/pull/556): There is now a `SocketType` enum that can be used when creating sockets. This is recommended over the old way of using integer values, which is error-prone. The overload of `ZContext.createSocket` that takes an integer is still supported, but is now marked deprecated. * [#559](https://github.com/zeromq/jeromq/pull/559): Added `recvStream` and `recvStrStream` instance methods to the `ZMQ.Socket` class. These expose a Stream of incoming messages, each of which is a `byte[]` or `String`, respectively. * [#560](https://github.com/zeromq/jeromq/pull/560): ZMsg instance methods can now be chained. * [#576](https://github.com/zeromq/jeromq/pull/576): Added an overload of `ZMsg.recvMsg` that takes a `Consumer handler` and a `Consumer exceptionHandler` for handling the result of attempting to receive a message on a socket. * [#586](https://github.com/zeromq/jeromq/pull/586): Implemented a Timer API based on the one added to libzmq in version 4.2. * [#590](https://github.com/zeromq/jeromq/pull/590): Added a `closeSelector` method to the ZContext class, to expose a way for selectors created by `createSelector` to be closed. Note that both of these methods are also deprecated; it is not recommended to manage selectors directly, as these are managed for you by pollers. * [#614](https://github.com/zeromq/jeromq/pull/614): Added a `ZMQ_SELECTOR_PROVIDERCHOOSER` socket option that allows you to define a custom [SelectorProvider](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/spi/SelectorProvider.html). ### Changed * [**JeroMQ no longer supports Java 7.**](https://github.com/zeromq/jeromq/pull/557) Dropping support for Java 7 allows us to leverage the new features of Java 8, including the use of lambda syntax to create IAttachedRunnable and IDetachedRunnable. * Refactored code to use Java 8 features like lambdas and streams in various places. See [#570](https://github.com/zeromq/jeromq/pull/571) and [#650](https://github.com/zeromq/jeromq/pull/650), for example. * [#510](https://github.com/zeromq/jeromq/pull/510): Polling now measures time in nanoseconds instead of microseconds, ensuring a higher degree of precision. * [#523](https://github.com/zeromq/jeromq/pull/523): Fixed a bug where ZLoop was closing the context. * [#525](https://github.com/zeromq/jeromq/pull/525): Fixed a bug in `zmq.io.StreamEngine` that was causing an infinite loop in the IO thread, blocking the application with 100% CPU usage. * [#527](https://github.com/zeromq/jeromq/pull/527): Fixed a bug in `zmq.io.StreamEngine` where data could still be present inside the handshake buffer that was not decoded. * [#535](https://github.com/zeromq/jeromq/pull/535): Fixed an edge case where, once in a blue moon, after a socket fails to connect, and is subsequently closed, it would still try to reconnect as if it weren't closed. * [#546](https://github.com/zeromq/jeromq/pull/546): Prior to this release, when a ZContext was initialized, its internal `ZMQ.Context` would be null initially, and the ZContext `isClosed` instance method would misleadingly return `true`; the `ZMQ.Context` was being created lazily when `getContext` was called on the ZContext. Worse, the internal context would be reset to `null` after closing a ZContext, which could lead to problems if another thread happened to try to use the ZContext after it was closed, resulting in a new `ZMQ.Context` secretly being created. Now, the internal `ZMQ.Context` is created upon initialization of the ZContext, and you can rely on it never being null. * [#548](https://github.com/zeromq/jeromq/pull/550): Various javadoc updates and improvements. There is now a `@Draft` annotation to identify work-in-progress APIs that are unstable or experimental. * [#552](https://github.com/zeromq/jeromq/pull/552): Fixed a bug where internal command messages (e.g. HEARTBEAT commands) were disrupting the REQ state machine. * [#564](https://github.com/zeromq/jeromq/pull/564): Implemented the ability to bind a socket via IPC or TCP with a dynamic ("wildcard") port and retrieve it via `ZMQ.ZMQ_LAST_ENDPOINT`. Leveraged this to make our test suite more reliable. * [#569](https://github.com/zeromq/jeromq/pull/569): Fixed an issue where an overridable method was being used in the ZStar constructor. * [#578](https://github.com/zeromq/jeromq/pull/578): Fixed an issue where an errno of 48 ("address already in use") would persist longer than intended in the process of binding to a random port. Now, errno is reset to 0 after a port is found. * [#581](https://github.com/zeromq/jeromq/pull/581): Fixed a bug where, if you're polling in one thread and you close the context in another thread, it would result in an uncaught ClosedSelectorException. * [#583](https://github.com/zeromq/jeromq/pull/583): Fixed a race condition causing `ZMQ_CONNECT_RID` to sometimes be assigned to the wrong peer socket. * [#597](https://github.com/zeromq/jeromq/pull/597): Fixed a bug causing the context to hang indefinitely after calling `destroy()`, if multiple sockets had connected to the same socket. * [#609](https://github.com/zeromq/jeromq/pull/609): For numerous methods, when invalid arguments are passed to the method, an InvalidArgumentException with a friendly error message will now be thrown, instead of an assertion error. * [#610](https://github.com/zeromq/jeromq/pull/610): Added some asserts in places where there could potentially be NullPointerExceptions. * [#623](https://github.com/zeromq/jeromq/pull/623): `Options.rcvbuf` and `Options.sndbuf` will now adjust `Config.IN_BATCH_SIZE` and `Config.OUT_BATCH_SIZE` accordingly. * [#634](https://github.com/zeromq/jeromq/pull/634): We are now using a 64-bit long, instead of a 32-bit integer, as a cursor in the internal `java.zmq.Signaler` class. This change should not affect the library user, except that it will now take longer for the value to overflow. Previously, with the 32-bit integer cursor, the Signaler could overflow within a month or so under heavy load, causing serious problems such as a server being unable to accept new client connections. * [#642](https://github.com/zeromq/jeromq/pull/642), [#646](https://github.com/zeromq/jeromq/pull/646), [#652](https://github.com/zeromq/jeromq/pull/652): Removed debug printing intended for development use only. * [#643](https://github.com/zeromq/jeromq/pull/643): Added some checks in parts of the codebase related to encryption and authentication mechanisms. * [#652](https://github.com/zeromq/jeromq/pull/652): IOExceptions that occur during polling will now set `errno` more accurately depending on the exception. Previously, the `errno` would always be set to `EINTR` when an IOException occurs during polling. * [#653](https://github.com/zeromq/jeromq/pull/653): `ZError.toString` now defaults to `"errno " + Integer.toString(code)` if a string version of that error code hasn't been implemented. * [#654](https://github.com/zeromq/jeromq/pull/654): In a low-level place where an `IllegalStateException` was thrown with no arguments before, the string value of the `errno` is now included to provide some context. * [#655](https://github.com/zeromq/jeromq/pull/655): In a low-level place in the polling code, `EINTR` is now correctly reported to indicate that polling was interrupted, whereas we used to miss it and try to poll again. * [#657](https://github.com/zeromq/jeromq/pull/657): When destroying a ZPoller, we will no longer close the poller's Selector, as that is handled by the context. * [#659](https://github.com/zeromq/jeromq/pull/659): Made internal optimizations to ZContext. The only visible change should be that the order of the sockets when you call `getSockets()` is no longer deterministic, as we are now storing them internally in a Set rather than a List. * [#660](https://github.com/zeromq/jeromq/pull/660): When creating a socket and the `maxSockets` limit is reached, a ZMQException is now thrown instead of the more generic IllegalStateException. ## v0.4.3 (2017-11-17) ### Added * [#470](https://github.com/zeromq/jeromq/pull/470): Added an argument to the ZBeacon constructor to configure datagram socket blocking behavior. The default behavior (non-blocking) is preserved when the argument is omitted. * [#474](https://github.com/zeromq/jeromq/pull/474), [#475](https://github.com/zeromq/jeromq/pull/475), [#477](https://github.com/zeromq/jeromq/pull/477), [#479](https://github.com/zeromq/jeromq/pull/479) Added features: * ZAuth, an actor that manages authentication and handles ZAP requests. * ZCert, an abstraction for CURVE certificates. * ZCertStore, a sub-optimal store for certificates. * ZConfig, to manage the ZPL file format. * ZMonitor, for simplified socket monitoring. * Reinstated support for the `ZMQ_MSG_ALLOCATOR` option. Added a `setMsgAllocator` method in the ZMQ class for setting a custom message allocator. * [#477](https://github.com/zeromq/jeromq/pull/477): Added an overload of `ZAgent.recv` that takes a timeout argument. * [#498](https://github.com/zeromq/jeromq/pull/498): Implemented `Closable` for `ZMQ.Poller`, providing a way to call `.close()` on a poller when you're done with it and free the selector resource to avoid memory leaks. It is recommended that you either close a poller or terminate the context when you are done polling. ### Changed * Miscellaneous Javadoc documentation tweaks/fixes. * [#453](https://github.com/zeromq/jeromq/pull/453), [#462](https://github.com/zeromq/jeromq/pull/462), [#471](https://github.com/zeromq/jeromq/pull/471): Fixed Android-specific compilation issues. * [#454](https://github.com/zeromq/jeromq/pull/454): Fixed an issue where the router was interpreting peers' socket identities as UTF-8 strings instead of raw bytes. * [#460](https://github.com/zeromq/jeromq/pull/460): Fixed an issue where CURVE keys were being parsed as strings. * [#461](https://github.com/zeromq/jeromq/pull/461), [#501](https://github.com/zeromq/jeromq/pull/501): Fixed protocol handshake issues that were causing interoperability problems between applications using different versions of ZeroMQ/JeroMQ. * [#465](https://github.com/zeromq/jeromq/pull/465) Various small fixes: * Fixed an uncaught divide by zero exception ([#447](https://github.com/zeromq/jeromq/issues/447)). * ZMQ.Socket class is no longer final. * Handle interrupt caused by close in ZBeacon. * [#468](https://github.com/zeromq/jeromq/pull/468): Fix an issue where sockets would disconnect when network connection was lost. * [#469](https://github.com/zeromq/jeromq/pull/469) Various small fixes: * Fixed an error in comparison of byte arrays in the Mechanism class. * Handled the possibility of receiving a null message in ZSocket by returning null instead of throwing an uncaught NullPointerException. * Fixed the return value of ZMQ.setHWM, which indicates the status of the lower-level calls to set the send and receive HWM, but was doing so incorrectly. * [#478](https://github.com/zeromq/jeromq/pull/478): Fixed an issue where, when using an XPUB/XSUB proxy, the PUB socket was throwing an error when attempting to send a message if all of the subscriptions have been removed. * [#479](https://github.com/zeromq/jeromq/pull/479): Various internal improvements. * [#486](https://github.com/zeromq/jeromq/pull/486): Fixed an issue where it was not possible to send two messages in a row without a successful receive in between, even with the RELAXED option set on the REQ socket. * [#487](https://github.com/zeromq/jeromq/pull/487) Various improvements: * Added some method name aliases for compatibility with the jzmq API, in places where the JeroMQ method names differed. * Miscellaneous internal refactoring to make JeroMQ code more similar to that of jzmq. * It is not possible to get the values of the ZMQ options `ZMQ_REQ_CORRELATE` and `ZMQ_REQ_RELAXED`, so `getReqCorrelated` and `getReqRelaxed` are now deprecated and will throw an UnsupportedOperationException when called. * [#492](https://github.com/zeromq/jeromq/pull/492): Fixed an issue where a NullPointerException was thrown when trying to bind on an already used port, for example when the socket has a monitor. * [#502](https://github.com/zeromq/jeromq/pull/502): Use explicit mutex locks to help prevent problems caused by concurrent access to a ZContext. This makes ZContext behave more like libzmq's zctx. ## v0.4.2 (2017-06-29) * [#443](https://github.com/zeromq/jeromq/pull/443): Fix issue where JeroMQ was broken on Android. Security no longer depends on libsodium and is now pure Java ## v0.4.1 (2017-06-28) ### Added JeroMQ is now based off of 4.1.7 of libzmq which means it now supports additional security features. ### Changed * [#413](https://github.com/zeromq/jeromq/pull/413): fixed a NullPointerException when ZMQ.ZMQ_TCP_ACCEPT_FILTER is used * [#412](https://github.com/zeromq/jeromq/pull/412): tcp accept filter null pointer exception fix ## v0.4.0 (2017-03-22) ### Added * [#366](https://github.com/zeromq/jeromq/pull/366): support for `ZMQ_REQ_RELAXED` and `ZMQ_REQ_CORRELATE` socket options * [#375](https://github.com/zeromq/jeromq/pull/375): re-added `ZMQ.Socket.disconnect`, which had been removed in 0.3.6 because the contributor who originally added it did not agree to the license change from LGPL to MPLv2 ### Changed * [#374](https://github.com/zeromq/jeromq/pull/374): * fixed a NullPointerException and mangling of existing indexes in ZMQ.Poller * fixed a Windows bug in Signaler * other small changes to keep JeroMQ in sync with jzmq * [#386](https://github.com/zeromq/jeromq/pull/386): improved deallocation of polling Selector resources. When creating a poller via `ZMQ.Context.poller` or `ZContext.createPoller`, the context will manage the Selector resources and ensure that they are deallocated when the context is terminated. * [#387](https://github.com/zeromq/jeromq/pull/387): (**BREAKING CHANGE**) It is no longer possible to create a ZMQ.Poller in any way except via a context. This is to ensure that all Selector resources are deallocated when a context is terminated. * [#388](https://github.com/zeromq/jeromq/pull/388) `ZMQ.Socket.setLinger` can now be called safely after a context is terminated. * [#390](https://github.com/zeromq/jeromq/pull/390): fixed a bug where terminating a context while polling would sometimes cause a ClosedChannelException. * [#399](https://github.com/zeromq/jeromq/pull/399): fixed a NullPointerException that would sometimes occur when terminating a context * [#400](https://github.com/zeromq/jeromq/pull/400): (**BREAKING CHANGE**) * deprecated the setters `setIoThreads`, `setMain` and `setContext` in `ZContext`. These parameters are set in the constructor and `final`. Because it is no longer possible to set these values after constructing a ZContext, the setters are now no-ops. * [#402](https://github.com/zeromq/jeromq/pull/402): added constructors for ZPoller that take a ZContext argument, thus making it possible to create a ZPoller whose Selector resources are managed by the context. ## v0.3.6 (2016-09-27) ### Added * [#292](https://github.com/zeromq/jeromq/pull/292/commits/12befcb27f13572a5a49669e433a399c3e5a72ac): support for `ZMQ_XPUB_NODROP` and `ZMQ_XPUB_VERBOSE_UNSUBSCRIBE` options * [#299](https://github.com/zeromq/jeromq/pull/299): a setter for UncaughtExceptionHandlers in ZBeacon threads * [#309](https://github.com/zeromq/jeromq/pull/309): MsgAllocator allows you to define how Msgs are allocated. * [#316](https://github.com/zeromq/jeromq/pull/316): ZSocket high-level API allows you to work with sockets directly without having to manage the ZMQ context. ### Changed * [**JeroMQ no longer supports Java 6.**](https://github.com/zeromq/jeromq/pull/316/commits/3cafb3babdb7509ec7adb705e1dacb6a804294a7) * Changed from LGPL to [MPLv2](https://www.mozilla.org/en-US/MPL/2.0/) license. * Related to changing license, the following changes were made as a result of reverting pre-0.3.6 commits by contributors who did not agree to the license change: * `ZMQ.Socket.disconnect` method removed * [Slight changes to the way ephemeral ports are handled](https://github.com/zeromq/jeromq/pull/354/commits/f455c740be4950ea7973276c33141008dadd97e7). * [#266](https://github.com/zeromq/jeromq/pull/266): fixed a NullPointerException bug in `ZMsg.dump` when attempting to dump a ZMsg after its frames have been cleared * [#271](https://github.com/zeromq/jeromq/pull/271), [#272](https://github.com/zeromq/jeromq/pull/272): misc fixes and improvements to ZAgent, ZActor, ZProxy, and ZStar * [#295](https://github.com/zeromq/jeromq/pull/295): renamed `ZMQ.Socket.setRouterHandlover` to `ZMQ.Socket.setRouterHandover` (typo fix) * [#301](https://github.com/zeromq/jeromq/pull/301): fixed [a bug](https://github.com/zeromq/jeromq/issues/280) where if a frame failed to send, it would still try to send the next frame * [#306](https://github.com/zeromq/jeromq/pull/306), [#308](https://github.com/zeromq/jeromq/pull/308), [#311](https://github.com/zeromq/jeromq/pull/311): misc byte buffer performance improvements and bugfixes * [#324](https://github.com/zeromq/jeromq/pull/324): implementation changes to avoid extra bytes being copied in PUB/SUB ## v0.3.5 (2015-07-15) * Capitalize constants * Use for each style * Issue #152 - Add unit test to test ZContext.close * Fix mislabeling issue * remove me from the AUTHORS file * Sometimes hostname resolution will fail. Make sure that, if it does, we don't break the ioloop. * Narrowed exception handling a bit. Makes me kind of nervous because the exceptions thrown are not documented. * remove email addresses * Fix issue #166 - Able to build using java 8 * Fix issue #166 - Fix building project in a backwards compatible way * Replace home grown Multimap with guava * Update README.md * Normalize license header preamble that mirrors libzmq * Add Trevor Bernard as a contributor and sort authors * typo readme * Fix issue #176 - Remove auto-generated ant build files * Overload Socket send * Fix style violation of unittests * fix bug where poll does not accept -1 as argument * Issue #176 - Remove build.xml ant file * Fix all style violations * Remove superfluous limit * Move the wcursor increment after the assert * Improve imports * Issue #191 - Generates excess garbage on close * Port JeroMQ to be based on libzmq 3.2.5 * Remove public method declaration in interfaces * Revert "Remove public method declaration in interfaces" * Change Chunk to be a static inner class * Fix raw type parameterized warnings * Change constructor and method declarations to be public * Update plugins * Fix issue where project wasn't correctly importing using new m2eclipse plugin * Remove redundant if * Revert checkstyle plugin update to fix build error * Add ZBeacon implementation * Fix checkstyle errors * Problem: beacon messages are not always filtered out for local addresses * Problem: current ZBeacon tests are not testing whether messages are received. * Fix typo * Revert "Replace home grown Multimap with guava" * Remove redundant static modifier * Remove redundant encoding entry * Fix java6 build problem where req was failing with BOTTOM illegalstateexception * Remove redundant method * Fix #209 - Set errno on SocketBase instead of throwing IllegalArgumentException * Fix issue #197 - Don't call setReuseAddress on windows * Change version to 3.2.5 * Make Mailbox,Thread and Reaper closeable * Router Handover * Rename xterminated into xpipeTerminated to follow libzmq * Fix exception for inproc bind fail * Fix issue #200 * Remove redundant nested static modifer from interfaces * Ignore .checkstyle file * Fixed two bugs in test path. In flserver3.java ZFrame.equals(string) will always return false. and in cloneserv6.java equals method is called on an Array. * Revert "Remove redundant method" * Test receiving correctly a prefetched message when using a poller * Fix issue #228 - Add ZMQ_BLOCKY to Context to get a less surprising behaviour on context termination * Implementation proposal for Z-Components: ZPoller, ZAgent, ZStar, ZActor, ZProxy * Fixed typo in Features section. * Aligned punctuation and capitalized first letter in sentences. * pom.xml: missing bracket * Change ZMQ.bind() method to return void. * Fixed minor issues - documentation (javadoc links, ..) - possible NPEs - simplified some statements, removed unnecessary variables, ... * Break loop on finding the first non-printable character * Fix issue #243 - Add a copy section in the README specifying the license * Fix issue #245 - Double socket close no longer hangs * Set daemon flag on poller threads. * Set daemon flag on beacon and zthread threads. * Fix Spinning in Reaper Thread * Added constructors to ZMQException * Changed ZFrame.recvFrame to return null in non-overloaded method * Added ENOTSOCK error code * Added EAGAIN error (code already present) * Fix resource leak at socket close * Fix c-style method name ## v0.3.4 (2014-05-15) * Various code improvements * Add unbind method to org.zeromq.ZMQ.Socket * Added double checked locking for shared variable context. getContext() and createSocket() should now be thread safe. * Extend support for ZMQ monitors to inline with jzmq * Apply checkstyle and sample changes * Fixed recvFrame to return null on no data. Added Test cases. * Corrected ZMsg documentation. * Adds lazy create context to getContext() method * Fix wrong Router xwrite_activated assert * Raise exception when bind fails * Fix issue #80 * throw an exception if the ByteBuffer provided to Msg is not flipped * re-resolve tcp addresses on reconnections * add convenience methods to set TCP keep alive options * Refactor Msg to better handle memory and Java idiomatic * Force StreamEngine to use big endian * Remove org.jeromq.* namespace and associated tests * Revert back to use currentTimeMillis because it's less expensive than nanoTime * Fix issue #122 - handshake now uses ByteBuffer accessor methods directly ## v0.3.2 (2013-12-10) * Various code improvements * Update junit to version 4.11 * Fix issue #115 - Expose all Context options * Fix issue #58 - XPUB can receive multipart messages * Fix issue #109 - Make ZMQ.Context and ZMQ.Socket implement java.io.Closeable * Use UTF-8 as default charset * Use monotonic source for time * Use try finally idiom on locks * Backport fix for race condition on shutdown * sendByteBuffer should return number of sent bytes ## v0.3.0 (2013-11-03) * [maven-release-plugin] prepare release v0.3.0 * Prepare for release * Update maven plugins * Change groupId to zeromq * Use the org.zeromq groupId * Add build status icon * Fix issue #95 - Add travis-ci support * remove usage of bytebuffer just for the sake of a byte array * use configurable Charset in every String.getBytes() and new String() * support DirectByteBuffer on socket.sendByteBuffer() * ignore whole target and also ignore Eclipse's .settings folder * fixes zeromq/jeromq#86 * Improved handling of ephemeral ports * Possible fix for a memory leak in Poller.fd_table. * subscriber should ignore HUGZ * support ZMQ_DELAY_ATTACH_ON_CONNECT socket option * Close inproc socket pairs on zmq_disconnect * Rewrite TestConnectDelay * Backport for LIBZMQ-541 fix * Fix issue when building with Ant and system default encoding is not UTF-8 * Update clonesrv6.java * Ignore CtxTerminatedException at ZContext.destroy * Fix issue #76, #77 but at topic remove at trie * Remove global errno * expose special purpose raw zmq.SocketBase * Work around for LIBZMQ-496 The problem is that other threads might still be in mailbox::send() when it is destroyed. So as a workaround, we just acquire the mutex in the destructor. Therefore the running send will finish before the mailbox is destroyed. * patch for issue 456 Do not filter out duplicate subscriptions on the XSUB side of XSUB/XPUB, so that ZMQ_XPUB_VERBOSE doesn't get blocked by forwarding devices (as long as the devices all use ZMQ_XPUB_VERBOSE) * Issue #72 resource leak at Reaper * Issue #70 Remove thread local at errno * Fix IPv6 address parsing. * added osgi manifest headers with maven-bundle-plugin * osgi manifest * Fix issue #66 - Add ByteBuffer API to Sockets for sending and receiving messages * Fix a bug that socket disconnect didn't terminate properly * add setTCPKeepAlive socket option * add a pom helper for the latest sonatype snapshot * fix missing frame at monitoring * Add chapter 5 guide * ZMsg.recv documentation of flag options * new timer during handling timer_event doesn't set correctly * chapter 4 java guide * fix a bug which unsubscribe doesn't work correctly * Set the compiler version to 1.6 * Suppress platform dependent encoding warning * ZContext.close doesn't have to throw an exception * implement Closeable on ZContext * user friendly error at bind failure * change jeromq package namespace and cleanup guide * Add set method for sockopt ZMQ_XPUB_VERBOSE * Add disconnect method * Ignore eclipse workspace files * rewrite poller as it compatile with jzmq * converted asyncsrv guide example to use the org.zeromq packaged code, and updated for the slightly different API. * fix constant collision between jzmq and czmq * simplify the ZMQ mayRaise logic * fix typo * make jzmq compatible and update examples * LIBZMQ-497 send unsent data in encoder buffer at termination * moving namespace from org.jeromq to org.zeromq * Issue#34 inproc connect should raise ZMQException * fix POLLOUT polling causes InvalidArgumentException * jdk epoll bug workaround * ZMsg.send returns boolean value * handle ConcurrentModification Exception * returns -1 with EAGAIN when mandatory is set and pipe is full * enhance device code * update README about 0.2.0 release * remove persistence related code * persistence helper encoder * start 0.3.0-SNAPSHOT jeromq-0.6.0/CONTRIBUTING.md000066400000000000000000000023101455771126300152520ustar00rootroot00000000000000# Contributing to JeroMQ ## Contribution Process This project uses the [C4 process](https://rfc.zeromq.org/spec:42/C4/) for all code changes. "Everyone, without distinction or discrimination, SHALL have an equal right to become a Contributor under the terms of this contract." ## General Information These [slides](http://www.slideshare.net/dongminyu/zeromq-jeromq) (a visualization of the [Internal Architecture of libzmq](http://zeromq.org/whitepapers:architecture) page) may be helpful if you are interesting in contributing to JeroMQ. ## Running the Tests To run the automated test battery: ``` mvn test ``` To run a single test class (e.g. PubSubTest): ``` mvn -Dtest=PubSubTest test ``` Before submitting a Pull Request, please be sure that the tests pass! ## Running the Examples To run the [ZGuide examples](https://github.com/zeromq/jeromq/tree/master/src/test/java/guide): ``` mvn exec:java -Dexec.mainClass=guide.hwserver -Dexec.classpathScope=test ``` Or run this [helper script](scripts/run-example): ``` scripts/run-example hwserver ``` ## JeroMQ wiki For miscellaneous information that hasn't yet been pulled into this document, please see the [wiki](https://github.com/zeromq/jeromq/wiki). jeromq-0.6.0/LICENSE000066400000000000000000000405251455771126300140400ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. jeromq-0.6.0/README.md000066400000000000000000000107251455771126300143110ustar00rootroot00000000000000 # JeroMQ Pure Java implementation of libzmq (http://zeromq.org). [![CircleCI](https://circleci.com/gh/zeromq/jeromq.svg?style=svg)](https://circleci.com/gh/zeromq/jeromq) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=zeromq_jeromq&metric=alert_status)](https://sonarcloud.io/dashboard?id=zeromq_jeromq) [![Coverage Status](https://coveralls.io/repos/github/zeromq/jeromq/badge.svg?branch=master)](https://coveralls.io/github/zeromq/jeromq?branch=master) [![Maven Central](https://img.shields.io/maven-central/v/org.zeromq/jeromq.svg)](https://maven-badges.herokuapp.com/maven-central/org.zeromq/jeromq) [![Javadocs](http://www.javadoc.io/badge/org.zeromq/jeromq.svg)](http://www.javadoc.io/doc/org.zeromq/jeromq) ## Features * Based on libzmq 4.1.7. * ZMTP/3.0 (http://rfc.zeromq.org/spec:23). * tcp:// protocol and inproc:// is compatible with zeromq. * ipc:// protocol works only between jeromq (uses tcp://127.0.0.1:port internally). * Securities * [PLAIN](http://rfc.zeromq.org/spec:24). * [CURVE](http://rfc.zeromq.org/spec:25). * Performance that's not too bad, compared to native libzmq. * 4.5M messages (100B) per sec. * [Performance](https://github.com/zeromq/jeromq/wiki/Performance). * Exactly same developer experience with zeromq and jzmq. * TCP KeepAlive Count, Idle and Interval are known to only work with JVM 13 and later. ## Unsupported * ipc:// protocol with zeromq. Java doesn't support UNIX domain socket. * pgm:// protocol. Cannot find a pgm Java implementation. * norm:// protocol. Cannot find a Java implementation. * tipc:// protocol. Cannot find a Java implementation. * GSSAPI mechanism is not yet implemented. * Interrupting threads is still unsupported: library is NOT Thread.interrupt safe. ## Contributing Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details about the contribution process and useful development tasks. ## Usage ### Maven Add it to your Maven project's `pom.xml`: ```xml org.zeromq jeromq 0.6.0 org.zeromq jeromq 0.6.0 sonatype-nexus-snapshots https://oss.sonatype.org/content/repositories/snapshots false true ``` ### Ant To generate an ant build file from `pom.xml`, issue the following command: ```bash mvn ant:ant ``` ## Getting started ### Simple example Here is how you might implement a server that prints the messages it receives and responds to them with "Hello, world!": ```java import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; public class hwserver { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { // Socket to talk to clients ZMQ.Socket socket = context.createSocket(SocketType.REP); socket.bind("tcp://*:5555"); while (!Thread.currentThread().isInterrupted()) { // Block until a message is received byte[] reply = socket.recv(0); // Print the message System.out.println( "Received: [" + new String(reply, ZMQ.CHARSET) + "]" ); // Send a response String response = "Hello, world!"; socket.send(response.getBytes(ZMQ.CHARSET), 0); } } } } ``` ### More examples The JeroMQ [translations of the zguide examples](src/test/java/guide) are a good reference for recommended usage. ### Documentation For API-level documentation, see the [Javadocs](http://www.javadoc.io/doc/org.zeromq/jeromq). This repo also has a [doc](doc/) folder, which contains assorted "how to do X" guides and other useful information about various topics related to using JeroMQ. ## License All source files are copyright © 2007-2024 contributors as noted in the AUTHORS file. Free use of this software is granted under the terms of the Mozilla Public License 2.0. For details see the file `LICENSE` included with the JeroMQ distribution. jeromq-0.6.0/doc/000077500000000000000000000000001455771126300135725ustar00rootroot00000000000000jeromq-0.6.0/doc/contexts.md000066400000000000000000000035521455771126300157700ustar00rootroot00000000000000# Contexts You may have noticed that there are several different types of "context" classes in JeroMQ. ## tl;dr: Which one do I use? Use ZContext. It is the current state of the art for JeroMQ. ## zmq.Ctx [Ctx][ctx], part of the zmq package, contains low-level implementation details of a ZeroMQ context. It should not be used directly in code that uses the JeroMQ library. ## org.zeromq.ZMQ.Context [ZMQ.Context][zmq-context] was the first cut at a higher-level API for a ZeroMQ context. Before destroying a ZMQ.Context, care must be taken to close any [sockets](sockets.md) and [pollers](pollers.md) that may have been created via the context. ZContext, by comparison, does this for you. ## org.zeromq.ZContext [ZContext][zcontext] is an improvement over ZMQ.Context, lending itself to more concise, convenient, and safe usage. ZContext implements the [java.io.Closable][closable] interface, which makes it convenient to use in a [try-with-resources statement][try-with-resources]. When a ZContext is closed, resources such as sockets and pollers are cleaned up automatically. ## See also * [zguide: Getting the Context Right][zguide-contexts]: general information about contexts in ZeroMQ * [ZSocket][zsocket]: The next evolution of contexts (or lack thereof) in ZeroMQ? [ctx]: http://static.javadoc.io/org.zeromq/jeromq/0.4.3/zmq/Ctx.html [zmq-context]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZMQ.Context.html [zcontext]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZContext.html [closable]: https://docs.oracle.com/javase/8/docs/api/java/io/Closeable.html [try-with-resources]: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html [zguide-contexts]: https://zguide.zeromq.org/docs/chapter1/#Getting-the-Context-Right [zsocket]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZSocket.html jeromq-0.6.0/doc/development/000077500000000000000000000000001455771126300161145ustar00rootroot00000000000000jeromq-0.6.0/doc/development/public-keys.md000066400000000000000000000231331455771126300206670ustar00rootroot00000000000000# Public keys The following are keys that have been used to sign tagged JeroMQ releases. # Trevor Bernard `` ``` -----BEGIN PGP PUBLIC KEY BLOCK----- Version: SKS 1.1.6 Comment: Hostname: pgp.mit.edu mQINBFbr87ABEADlGUcBcD1OiSISJBvbw1F4K8afQGrjTv3eUMaiuNYWIfQUKnkr3iGEzYX7 qaZSQiulS4pBt6MghyXPzDSzJY4XaCr5z/EiZICXfcTHrMx2s1RGFHuBhMu3v2CgnsYyuJBZ OFp3PzzfIwbzS/LzETrbUxBKlrUuwtD+Z2+aQqfGFK9hwApx2vIZ+J+/IEY67d3iX5Lw4Q5a 3QsZwIB083E8pFXxqKZTzifZiGVBUfsAzNu+aZ0D/tZB25FU1UHdYArP552TTYMyNdoRj/n9 27CVWvvwhCkXuKU6uBjMlvhfeEy6g+SDVZcgYf+trOi7R3LZ8tBEriaRahlIqJeFQScVHBHV 1jpjPDgNUOc/iBrmNsmHv5WY8AeYdOxlteL8baEe4/tXIkyT8aKjQIeyTAzhaBqK1CD5ZbI+ b5T5OMhxEAHyxV8THyjso0GvkiFrM1Su14yTkDU7IN1gxhKneIbtlnb8CC/bNg8AZWXtBgyN 34nsdj5vXSDtFKfOEt5lmivjojtIj8ODNFKsPijY0SOdQMZjGejpHInDDgPpyhVL9TkH763I 6QaG37P/05Vrozxi1yHVcX/vGtUQWgMdQr3X7oRxeqrFcD/nLpRNyweSmIYYvXruEpXsQwOD Faw8cfum0v0GmchLjlZo0YgyfYQus+YSmfZXRMr0Fvd+1FjmuwARAQABtCJUcmV2b3IgQmVy bmFyZCA8dHJldm9yQGJlcm5hcmQuZ2c+iQJOBBMBCgA4FiEEai+vi7G86MrrvwUhQFEOtPYc TJ4FAltZ4OoCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQQFEOtPYcTJ6HWQ/9FeFN ZJ8jm99w1JiaxAiFYqIKF+A17Ht9KFO5yo1y46mTilr+HyXdfCv7MGpV1Dw4Ba4LaIX4Kd/8 snOvD7uRjlOgtORmkb3P7scMyn7+GaTZpT+C1pSAVbXFb4YhABALlo1CaEvMR2WZLhvj3AxY PlmwwcSXScfyPHOJ+LZ6AQguqvC/siedyHaIUUdIGkXITtRYeljJpEUK9k+CRER+YjucedlA U7z0gZhjrP3RSbVV1curyKsEte5VtUzvMe9DpbAPeF62VnvlEAiC4ow4u0m+C6vi/Ybd4JuF x5TgWXtDdJmdIzgYjs49fib/Z+GCi3ryTqHSxQFb6GRc6elF3c6Y55bVDagGxGdI0ehBVrsM jAu/9CHl2RduNq/gZz8eODHZQgbgnLxCjbj96bXb0LR3A1TuH65sROC2Hsj+2Fh3cfSjZxlp Xusf2qxkzEig+YBt7m7ZUG68/+OkUlut1r5gd7TH2RAKUjxCqtjLdtplIc90mmXhwEzjEIHt LXBDocyT7RGzciY/wqFZ4GQGQDAlmckQW7zZpzvT1vWBYRe6UcpuvuI/lTJ1Clz9uIkPrFgy 0mrWSThKPrWKgetgLBsJYNBMsRYr7V9sVSQ6OkHmBNNdzNq66tFD3ynytvMIyHbgzM5anTvn uEjhFg3+b7iXdrcFEF3wjlat12mpNpC0I1RyZXZvciBCZXJuYXJkIDx0cmV2b3JAYnJhdmVu by5jb20+iQI5BBMBAgAjBQJW6/TCAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQ QFEOtPYcTJ5h2w//T9yrwDX9bESiLxhiPp4kXX1oVQmMY/Zv8J3pYGFxXdfa6qlGkeK61qTx bilpaIYfEPa6lXYN7JT5G4lmzrhijqBxeGV2Y9p7WqUNBB96IONDcm8duSC8XVRZWjy8rYge NGgTmpe3jLpbbkQWddbHtipqAlIJbiWCy6ZSW/okLASk7pKx5adBzQHGHdwmbtoddArEX4jd Ws+GT9HzHI2DnI6sN5s809AlyFJ8fc0TVzgZUMgdZp3LqzNBuqP34Mi/cWytAbNQv9xTWfY8 /XHEazavnAWmGTjHX47jJd8JwTHxU4akjNj7Oa0an7uSBhlLpNts8mCbHam3JZ4uEaqdxW/w cM5RURgSfCI04P6P2sNGQ0xVA6hwiOKgNofYpPUDWtIDN6Sb4KlKFcBe1fCCVD4FRF2rbXob vCpqbvk0yAnrADprIvzO9C2GEAWRjmxItO8Z/UKD6MpmRdkNlCK/M3uoaLj2dw8Pk7HLNHhz b9HrWBOKxhyfu6zHJrP7kGzQ1/E4ekjbkLTKUyl05ghY2w5zZu40ZdiQtPPMkFX4mYyQr7TT J5vKne2fMu6ZUYwaPDSOKSdiZQnerbfPfyYpxaVUb3MVeDgKbIubKsMzWDx6ykaV2JEATVfC KSEhi6RscXW+NaVzP35ayGLz46rJzaV996/tk5BMGitk5fFOw620KVRyZXZvciBCZXJuYXJk IDx0cmV2b3IuYmVybmFyZEBnbWFpbC5jb20+iQI8BBMBAgAmAhsDBwsJCAcDAgEGFQgCCQoL BBYCAwECHgECF4AFAlbr9WwCGQEACgkQQFEOtPYcTJ4mxw/8CZOJ5LtU1tAkByVE+xk+Hw27 Kpgj/xiIJTfoApy9+MAWO0VKlbSTFbZF7PO/qrdbYg+ZDWYDX79kVwsj5ubtwbMRNDbgfUKz tEQisFNdh98QQuqmqApi3yZVtdngSxubr6Du/1ggAIxIPB0Z4IBZ6m251UqYE3OPP1seUPij 3PG06ghyOzgDD6FrCFoG/VqElYX6FSCM6Q4tlNTG2f55Rf1ylHfdpiyumpZkPxqc4o3odf8g bfLX/XTNYcUL54du3WelA+Q82EzSj5/xKwsh90K971oOkAQWSHpptJ+FB61PE/D4zyc+CCAf MhnGcSbRCFhU//XDkc3M4OCKvE87onTndgmfX3jjQyE5QzeKol9CyJ9RdQ7okM41EmyDmykB 6X5aFoDs7ubjX6gDp9B42R/ZqqZnQMsw/n/27fjwhjcUm73UgQ0nrQl+G9QGZg4RY0+x0uj9 pUjRiD+GVSrzBzk0UV6rYa8tHOZ4PQoVwN2ejL2xINoDBdLIB+njuir4Zkw5ZGDhoLGCsj7C LH18JbPRtYbsPZIvWrADhx34EZegdgpn0Una6k5ZjW7XjTIYXeeGdD0yzXRwKNAJ261g9pvZ 2sjYeBuhaTzGSMyB0DfmkGto0vYeIv9+K0Nl7VTJgSYnmphorAycEtx8FqyVPx/GH/ZyPa1c UMKQTxxEiFK5Ag0EVuv0RQEQAJdQ8qYFTEyZS12awcOFe8Rq8orFSzHm4DqZBeZeIv4ieEeA 5KRwsSFUn3dR8sZpjZzM1/rL7jXGYhnBxvU/D0Y1bpariBvv7VsFoP8Z931/OB+HjM/EtRpA yqyIhN/IQaA8U3cZx50ImhRlLcEUhPUa5v4q7LTBL6BUeSQkfmdXC5YUpR6uk9DshqS2WmG6 S3HEJNpr3D4/SbI1HuOKV2lixnbuaFP24JpE475jjEySLaBS7yjXgKpugjAB0ay6ygIfkAA+ lXqUTjlIc4nSC0jJdFdRm13oYgGWL0vQuSfSi9ebbEGB0vXKYe67C50l1rVGOw+ekJK+CYke 3juXEDUSStrXw6FfNzzvR7BdYF7XlxyQx4XzL85AYrJkHE4OgwCItHBaB4II0cp5zxJCuatx WTCT+ibb2On+qhZ8lnSqTI4rwo8GvQvl1b9kZCx5t4QUe+tRoy0ySczeA+t2XG5KBHpz3NSZ wSnedKpf/F1/lqdn42vE18A6aey090KHIiI4EepenzciMOpE1wVjH2GIQFKP7DS/S3/kcCBG Hrxea4L1XUvXlcUxtbxClJUtby9t0CJ9UaIEhV/rViCSMpdROtkSk22wtpAybacLtXLq5oB4 zQb9cHz/WCwHuwXr2MKmxQau7QUaVQg3BknqcxFq4kARFtdK7LQSayhAm6JBABEBAAGJAh8E GAECAAkFAlbr9EUCGwwACgkQQFEOtPYcTJ5qyw//XZpTqlqM8JUT0r+N6kG8Dw/veFUcHR9+ DeqCAFSInOxZXjeSjCT8G0HfvL3JQTexpqyj9Spyf7zZRMPUDhric87xrl5Jfs4RPvSHImjr 3Y1ZVnxwYQoA9aDI2NsQsIdIVXbkCa7ApwjI/+vjmqtq4sNnFkK+mAeBA+cc+HyuIPWs36cx j44JvqCRQvth4KybYoKshbWc1E+1AZQcX5vObkJj0PPtMdD11RfZs+2MveLAkKAotwgDgIKu a28c4Km5HNK5jMxGGJd5luBhW9sGKCv7xU1fJ4cilarGMlZnGxyR27IalnrpprUavoPFBBNA T0qzSS6B+yK1vzVRN96lMvbh8KKjOdhrTZgf9+5pPxctLwcBiMVHObfPn+lw8YTMVnqmqFir XbCz1dU0edf888Jf5rtbYyyxRTgNFzf1+F/PAs3UqqDKOm+2bPftrauqzwO474Rp2QhHJ78H 6UJIxKH9rKvP12K3wDY20q2d2gs7RF8qO6MNzOqDUXhpopH6pPEThlUIcvFVozIegXnESpj6 c0t+OEktEpnHjWkFkgJc6/sKri3YhCd4cKFcUs7j7NGe5AA2jNgNILMMzzl2AntAWyO5jIZY n4UScSj8B4YVj9zsZ/1tp9rQIyPBIgweD5hw6P/hp6HQD6bE2NwmZjuloAEC+tgYrp5rnLpJ JWO5Ag0EVuv0fAEQAKQdZ7EJg/6HyQ6IUus40bOlIIhO+QjQC7iO+kddYlPwHaV9UWZS0/u9 I5a0SZrxRuoZYe0A2pThDW9mfMgoZFqUFY3j+HN6rFxDcwB1upHjqTf7N2Az6kGGsQ9ssOsa qKaKYclrqNQtwKlR9lv4jqE2JuyzQKDV1WOcDzQ+Svo7l/RWZPDWF23fUXG6LhNIvVcNHrnb SAgn8159/cqAhaR++zlzzBNGNN44l4n+qqnIui62K1Z5t3I18W0NJjW58ZvMw7Fgd5uAfxox pkuXXkpt2PEt8nESadMZkK1GjBmA+cYPWSTHTPhmEv0tUAiA7bEwZxleKOORyT7pjYxrKPF/ /zcRslePwjokjr9XuQfSq9iIc54CUnDuYH9fl6oUorPSpUBZWOJ2RG2mj7odhpOkBB/1WxCp EoMfMdi1t0jIvUuM+R6UMQryOlp+Yrft8HJ5kJSRY9uh2Ms3N++Z1EYN0C26T2r1+cJRh2IP uwJrnMLjIIdW+e0+0RLpW11ivHEbeHHRWSxkqgx2ZpZ85z5gs2Tke0IUbv/B5zqqyZQ8LgPP TM6igbBySq7TyBbp2217DsQALTTtiSLErQKMmYsfw192r8iL7+enY6GvvxpJONdCLLlU/GCC 2XW/s6SYmsOG/cKz7H/SAWtCXk9kWnWQ14f5inuEJwm8fOIjVw5/ABEBAAGJAh8EGAECAAkF Albr9HwCGyAACgkQQFEOtPYcTJ69iA//dySDbEQlan3wjW4OqtloZ2JWeR7XNP6m3clgLWcm s61bJeuXq+UABrzw0V/TS7bYXvTvcKiyHFukdZ2dy8RwF9X6laEOlNOeekoUHlZ6c3WrHVk4 4jdRWuav2I3AriX+TpfDVlpLVwSNQ7/XtTGCwqJe07tKVo2bUK5eldZqFVd2jFICWiinnylb 5dbn2lhD05kxhKHeM8zn4T9MwW+uxwJp3gyM8NmO7YCUIDI04tHGTDyVc4cU8lsJ5GDE7xKD GJ7Ky/0XOPqaKIp4/v9sc7MhaUPVe0LhWUuCcpUA1o+e3JK5FDZA5aER52nr4c8QxxD0BqGM 2yOMhdDdSwIA3v1I5uyGaCh6VTbrO23na9SaNMaD33bEeGR5yIF2q2w9pOHrpc6AagQkrpCJ GZz48lKR7iPSryBnxae0+bEWMzJstQddcwRGvgDJbN5nH0+v4cyGUhLHgF4+Cd8qhzTLzUqy ynuBiOnfflvTom3VGzCZ20B8nmmRVaXZAj4GBMe6ne4EZoKTkwMXpy7IZtGgFK/dToBR3J8F 0oKiGyeejJfw5NWZXRJxqybAU2si6249fDSZnwRa40SziMQLz8t24bWw96Mvm7eW+Eon3FkA 81bEDnfO2Qz+uP9m7O0ltyEbYBUPul1Q4aMSmdJprd8U8ugxet7TBssyF1MXMMGovCM= =xU/T -----END PGP PUBLIC KEY BLOCK----- ``` # Dave Yarwood `` ``` -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFSbkukBCADjJ9bL6Dv7Fbw5eLkwbZNGBH9pE5OKnGzGVI6yKgZ5iZqbMLe2 HRxkbOYFrAHWJm6yC033HtIQYn8w4IpC4tRBtxMkif7GSH81oJ3BCYHrA6Jy9DSN 1knOrcEC/aWvgVsJHt6iNuRgSHOnZwUNdjTU9FBNIQxksWwNPlBUqufvF5Aqo7gL RZEdnwNvzibsZGH0Bg2y1wSdEBdg3swvF7hUjuMreqbv+f2wThtbThz/qDO8ONpJ aIiVTPiL7qas/4H/eofzaa0UzlNdl/1S36DXPkHdFZLa/gzGXTpHscCiwLICAxZT 4Wm+LbPK/cjEX+yyKNpRkjdcFc+uW2ci3oFZABEBAAG0JURhdmUgWWFyd29vZCA8 ZGF2ZS55YXJ3b29kQGdtYWlsLmNvbT6JATgEEwECACIFAlSbkukCGwMGCwkIBwMC BhUIAgkKCwQWAgMBAh4BAheAAAoJEBdPiDGLZMsCF7IH/RSFm9TEcLdCtbNCPmIB BCmg7ThRv6oJdbmUaFrmvXcFEKzgo8cs19WObAjglF/BXaIMEsZOybVWiqdaMfip k9xbdBCrcAck3MJb26ffRT7nfsPcVHJPPYJ4ix7rgBvLZ6yvHguZN5MCBAlPeigG dXMPVTHZDDVlA1Fd2zZ0Y/SLXHfWt6/qKFizkLdao6GpCebWDfDXj3AaIAi6sOAN RTebX5TbL/QPdCGralSL2Qar0dlbodQs9vw3+3fUjYosTfxTvvBn3C0hezwVu7o1 sKAGazgY+NNyN0SMRYDZfE6j0G4q7N1a3bIELVYNwAA3axlVnsQDEpY3aV9ShDbh KNm5AQ0EVJuS6QEIAOaF3sc091G9isyF+5dm2vjMy84wsEiGbDZ0GjKbYieyX9mt ZlwNLy8IV8cyxBpJrjhzKAp6RhVyMakRjKfcOYbVLzGrnKJbV5jq6ymwXNumgiLg kL2liYMkgVtKAuEKhvdGWToqXwuVSeF9WD9qAZ30bOg/HMKfbmYbgrA6lSp+u7J7 Ca+AaCHk2gXqVbM/H6M1hOhLF4pml/peWh7Kndt//QZOOU8WD9l25jKklluGA0pI TGl56mVjHbrNJ+MRKsGBBQIgBjD+Fh3BcJvZeawNd4eacs2ZCamamMtNyxXJh8X9 LQTBQpuwhBB9b0ZiSFeoWGkTJss+y9CpkPJmsC0AEQEAAYkBHwQYAQIACQUCVJuS 6QIbDAAKCRAXT4gxi2TLAkBpB/0YMnFcX9fJ2aP5ZuP0P84dL92yo+tFylHHYpwv ZFIq2TdTOYDNhxD/yuvEM7HMp86dV8DiqpZ+6QRP2Ooy5KDEv8YZV+mWhndNZauX ecfngcoM448XF8Bgdft7mBNBuOJteiTQ0bsLuVqPyXbZFXgoiUf0+iJ7eCNl8rKe TwOBZR4IpHkW2p9ptF8764L4eNStWyYWVrq7eu3SIKWt/pHUlZoK+hMxyZe01QSi 3SB3mw1jM3W0pqVKIHX+DCp45L4VPZ9fplyTsExkPS548bnVIC7+QxIYQco+36kK PuMjF5XJV9daCDaTXlkDTDoGFcLwi7gFWx9+H5HMxCtQq+bX =evFC -----END PGP PUBLIC KEY BLOCK----- ``` # Automated publication at CircleCI ``` -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBGUFZeEBEACl/bYA+BvSw5YyqNJ+KDzJbx2DD4i/JXYyT2Z83bE8CYBhAegm Ygiql4ejYhb1opWPIDopVXZ3DOyMivvS68foQ6UA+4ohOydaqTAHPRnn/+5GZFFM kfuA7KLZhEYRP6wkEmRNm63q9TIg4b80nssXbWKHNAar9VWk3yIKgsRsSXf1o9bh IrbCPh74qyFd0aWDbLUdZd1YFPT67IQbdPkioLp5TzPAHLuOp3qZuQEOu7ouRI6S P7eW0nkSIDcGRG41mgeveTfduIM3HxayTAiKsooBfsEtzVIsczW9qFP2wJ3ZiZt5 iMj6WFH7J+dQYXndZF4r7iFB4M/tSOVhKUmSLoJgdF39h4WolCuICGM3DlmshPHG rrTNcrACDSm1bYbG1UG45cBexNflyjGHyUua9eAqjrBmH7IUHiiLY8mTx9kTy8Ne DK8y9XEDxv2M80k8h5JPXT3fxhNkqb5swrXx5gtR+tmfwg35tulmfKPcNvubQgOO 5eQ0ansIc6wGgRZ6rVemcL/LNNLrCP4tok+WpzzD41bw/deEMNuTxwsOX9vMy8UM 9RtwxBWQMdgAvxaqQvZPCzxqIlVGcYFsxyIPMNbBwb9DjX0saQzQCASZcuiTDNGe 2YmzG7UT1ka90clfFh8a3MDTE5YtgL9xw3lpS9Zsjsbd6r8jMiGFcgrfDwARAQAB tAZKZXJvTVGJAk4EEwEIADgWIQQiOUvDYhLtNpSu+Nz7LqA2nJJe4QUCZQVl4QIb AwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD7LqA2nJJe4f1xD/42o8a9adQ7 kGpU5+PlqXrGquo/Rb2sgvSa+snlDb+wPeyh/P5fPz2sl3crUcH1mMXGWoXhOega daaK0JVrdkJwCYODnMtmdLirChjrcUCsG2fjwJFDOCRGe19wyaf+vX2gRnSQMcaN 86q5MMxh3RXzKwdQWoIVyMuf32G8ysOATLBY4cpuAAjAVm9TH3jnFf0Y2+6gDx8W uvFThnLcyOCRiHnKNCitrEbEox0XYyLQFg6YNl7H74KhhyB6W+bmIs/XUVnybh2I 91psjCXEQo76vjEFqTz7aQFvfIvuWxY7Nj8PcKXcIBJEmmHzdJVfP5ptleraFT3n 7vh4RJpjZzLtn/m1JdvwGQpuzHBZbwMSBxee+lrTlICBtbV97xIdaYK88ZMm4MPp sOEs5IK4YlWN4HViVKCwkknHbL9mGH0bom5cIxsALMll+Y83s3U8mCQu0L22Vznv q+Xz85ly093Xmrw56En4ZCsiXrxHwH10P3+w6BYZSf+n2vCfhisy9Rz7eehkz/FW RIeX8efcToS77+JXKjl2wCPIBrUMyh/45HaucyYMqr+SqGq8ZKlSqbweZy+JFM34 ur4bky72JDdhJe/q3Xkkl6C1ipkoqPWzA0neUbFxRm0CBQ1oU6q1bmdJLjofWtMM 5h0FJi1Lc66uQe2stHEPfr+wlocfLDKE0A== =bpzb -----END PGP PUBLIC KEY BLOCK----- ``` jeromq-0.6.0/doc/development/release.md000066400000000000000000000044171455771126300200640ustar00rootroot00000000000000# How to Release JeroMQ on Central ## Prerequisites The minimum supported version of Java JeroMQ uses is Version 8. So when publishing builds to the Maven Central be sure to use the latest stable release of (open)JDK 8. ## OSS Sonatype In order to publish builds on [OSS Sonatype](https://oss.sonatype.org), you must first have permission to upload on behalf of the `org.zeromq` groupId. You will need to create a JIRA issue on Open Source Project Repository Hosting (OSSRH) project requesting access. Here is an [example](https://issues.sonatype.org/browse/OSSRH-46351) of such a request. A current maintainer must approve the request before you gain permission. ## Sonatype Configuration You will need to add the following configuration into the `~/.m2/settings.xml` file. ``` ossrh ... ... ``` The username and password are the same as your OSS Sonatype credentials. ## Release a Snapshot to Central You are not required to sign SNAPSHOT builds. Issue the following command to deploy a SNAPSHOT. ``` $ mvn clean deploy ``` ## Release Commands ``` $ mvn release:clean release:prepare ``` You will be asked a series of questions regarding version numbers. It is safe to hit `` 3 times. Example output: ``` [INFO] Checking dependencies and plugins for snapshots ... What is the release version for "JeroMQ"? (org.zeromq:jeromq) 0.5.0: : What is SCM release tag or label for "JeroMQ"? (org.zeromq:jeromq) v0.5.0: : What is the new development version for "JeroMQ"? (org.zeromq:jeromq) 0.5.1-SNAPSHOT: : ``` The Maven Release Plugin will take care of bumping version numbers and tagging the release build. It will also push those changes to your chosen SCM. When you commit this changes, CircleCI will pick them and publish in the stagging repositories at [OSS Sonatype](https://oss.sonatype.org), with a valid GPG signature. There is a list of known keys that have been used to sign tagged JeroMQ releases [here](public-keys.md). To publish it to the Central Repository, one can follow the procedure at [Publish my artifact](https://central.sonatype.org/publish/release/). # Making an Announcement on the ZeroMQ Mailing list when it has been successfully synced. TODO: more info? jeromq-0.6.0/doc/exceptions.md000066400000000000000000000020271455771126300162760ustar00rootroot00000000000000# Exceptions JeroMQ defines a handful of custom exceptions, which are thrown as a means of signaling various exceptional internal states. These exceptions are defined within the [ZError][zerror] class. ## ZError.CtxTerminatedException This exception is thrown when an action is attempted which requires an open context, but the context in question has been terminated. ## ZError.InstantiationException > If you know what this exception is for, please update this document with an > explanation! > > Anecdotally, I can't find anywhere in the source code where this exception is > ever thrown, so perhaps it should be removed? ## ZError.IOException This exception wraps [java.io.IOException][ioexception]. When JeroMQ throws one of these, it is an acknowledgment that a java.io.IOException has occurred, but it is not JeroMQ's responsibility to resolve it; it is up to the caller. [zerror]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/zmq/ZError.html [ioexception]: https://docs.oracle.com/javase/8/docs/api/java/io/IOException.html jeromq-0.6.0/doc/monitor.md000066400000000000000000000041501455771126300156030ustar00rootroot00000000000000# Monitor ZMQ does not provide a logging API, but instead used monitors for notifications and debug. It uses a dedicated socket that will receive events from all sockets using a custom serialization, which is not compatible with the one described in zmq_socket_monitor(3). Or it can also handle directly the event in the caller’s context using a hook that consumes events. There is 4 classes handling events. ## zmq.ZMQ.Event It’s the low level implementation, close to the C implementation. It doesn’t try to resolve argument as types; they are simply integer that needs further processing to be resolved to high level objects. It provides a `zmq.ZMQ.Event.getChannel(zmq.Ctx)` to map an internal file descriptor integer value to an effective `java.nio.channels.SelectableChannel` object. If used through a monitoring socket, the status of the channel might be different from when the event was generated, as processing is asynchronous. A hook that consume those kind of events can be declared by using `zmq.SocketBase.setEventHook(ZMQ.EventConsummer consumer, int events)` A socket that will received serialized events of this kind can be declared by using `zmq.SocketBase.monitor(String addr, int events)`. The address is the endpoint of an IPC PAIR socket. ## org.zeromq.ZMQ.Event A first try at implement a high level wrapper. It is not very consistent and being a nested class reduces code readability. ## org.zeromq.ZMonitor.ZEvent Another incomplete implementation of a high level wrapper. Again, being a nested class reduce code readability. It also stores the value as a String, which is not very usable and uses `System.out.println on many places. ## org.zeromq.ZEvent A more advanced implementation, that return high level java object whenever possible and is more readable. A hook that consume those kind of events can be declared by using `org.zeromq.ZMQ.Socket.setEventHook(ZEvent.ZEventConsummer consumer, int events)` A socket that will received serialized events of this kind can be declared by using `org.zeromq.ZMQ.Socket.monitor(String addr, int events)`. The address is the endpoint of an IPC PAIR socket. jeromq-0.6.0/doc/pollers.md000066400000000000000000000027251455771126300156020ustar00rootroot00000000000000# Pollers There are multiple classes implementing polling behavior in JeroMQ. ## tl;dr: How do I construct a Poller? Use [ZContext.createPoller][create-poller]. This returns a [ZMQ.Poller][zmq-poller]. ## zmq.poll.Poller [zmq.poll.Poller][zmq-poll-poller] contains low-level implementation details of ZeroMQ polling behavior. It should not be used directly in code that uses the JeroMQ library. ## org.zeromq.ZMQ.Poller [ZMQ.Poller][zmq-poller] is the user-facing API for working with pollers in JeroMQ. Pollers are constructed by calling [ZContext.createPoller][create-poller]. This is essential because it registers the poller with the context, so that when the context is closed, the poller and selector resources are cleaned up properly. ## org.zeromq.ZPoller [ZPoller][zpoller] is a work-in-progress rewrite of the polling API. > If you use ZPoller, please update these docs with more information! ## See also * [zguide: Handling Multiple Sockets][zguide-polling]: general information about polling in ZeroMQ [zmq-poll-poller]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/zmq/poll/Poller.html [zmq-poller]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZMQ.Poller.html [create-poller]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZContext.html#createPoller(int) [zpoller]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZPoller.html [zguide-polling]: https://zguide.zeromq.org/docs/chapter2/#Handling-Multiple-Sockets jeromq-0.6.0/doc/security/000077500000000000000000000000001455771126300154415ustar00rootroot00000000000000jeromq-0.6.0/doc/security/curve.md000066400000000000000000000007151455771126300171120ustar00rootroot00000000000000# CurveZMQ [CurveZMQ][curvezmq] is an authentication and encryption protocol for ZeroMQ. You can use Curve with JeroMQ, generating keypairs via the [Curve class][curve-class]. For an example of this in action, see [this gist][curve-gist]. [curvezmq]: https://curvezmq.org [curve-class]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/zmq/io/mechanism/curve/Curve.html [curve-gist]: https://gist.github.com/trevorbernard/6e3a8af0092cdced0f8b3e757a5b6b16 jeromq-0.6.0/doc/sockets.md000066400000000000000000000023171455771126300155720ustar00rootroot00000000000000# Sockets There are multiple classes implementing socket behavior in JeroMQ. ## tl;dr: How do I construct a Socket? Use [ZContext.createSocket][create-socket]. This returns a [ZMQ.Socket][zmq-socket]. ## zmq.SocketBase [zmq.SocketBase][socket-base] contains low-level implementation details of ZeroMQ socket behavior. It should not be used directly in code that uses the JeroMQ library. ## org.zeromq.ZMQ.Socket [ZMQ.Socket][zmq-socket] is the user-facing API for working with sockets in JeroMQ. Sockets are constructed by calling [ZContext.createSocket][create-socket]. This is essential because it registers the poller with the context, so that when the context is closed, the poller and selector resources are cleaned up properly. ## See also * [zguide: Handling Multiple Sockets][zguide-polling]: general information about polling in ZeroMQ [create-socket]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZContext.html#createSocket(int) [zmq-socket]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/org/zeromq/ZMQ.Socket.html [socket-base]: https://static.javadoc.io/org.zeromq/jeromq/0.6.0/zmq/SocketBase.html [zguide-polling]: https://zguide.zeromq.org/docs/chapter2/#Handling-Multiple-Sockets jeromq-0.6.0/pom.xml000066400000000000000000000327501455771126300143510ustar00rootroot00000000000000 4.0.0 org.zeromq jeromq jar 0.6.0 JeroMQ Pure Java implementation of libzmq https://github.com/zeromq/jeromq Mozilla Public License version 2.0 https://www.mozilla.org/en-US/MPL/2.0 git@github.com:zeromq/jeromq.git scm:git:git@github.com:zeromq/jeromq.git scm:git:git@github.com:zeromq/jeromq.git HEAD 0 UTF-8 8 ${arg.line} junit junit 4.13.2 test eu.neilalexander jnacl 1.0.0 ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ org.apache.maven.plugins maven-surefire-plugin false 5 org.apache.maven.plugins maven-source-plugin attach-sources jar org.apache.maven.plugins maven-javadoc-plugin attach-javadocs jar biz.aQute.bnd bnd-maven-plugin 6.4.0 true default-jar jar Bundle-SymbolicName: $[project.groupId].$[project.artifactId] Export-Package: \ zmq.*, \ org.zeromq.* Import-Package: \ com.neilalexander.*;resolution:=optional, \ * -jpms-module-info: $[Bundle-SymbolicName];access=0 org.apache.maven.plugins maven-release-plugin v@{project.version} true false release deploy org.apache.maven.plugins maven-javadoc-plugin 3.4.1 true ${maven.compiler.source} api.note a API Note org.apache.maven.plugins maven-antrun-plugin 3.1.0 org.apache.maven.plugins maven-clean-plugin 3.2.0 org.apache.maven.plugins maven-dependency-plugin 3.3.0 org.apache.maven.plugins maven-resources-plugin 3.3.0 org.apache.maven.plugins maven-compiler-plugin 3.10.1 org.jacoco jacoco-maven-plugin 0.8.8 org.sonarsource.scanner.maven sonar-maven-plugin 3.9.1.2184 org.eluder.coveralls coveralls-maven-plugin 4.3.0 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M7 org.apache.maven.plugins maven-source-plugin 3.2.1 org.apache.maven.plugins maven-jar-plugin 3.2.2 org.apache.maven.plugins maven-assembly-plugin 3.4.1 org.apache.felix maven-bundle-plugin 5.1.8 org.apache.maven.plugins maven-site-plugin 4.0.0-M2 org.apache.maven.plugins maven-project-info-reports-plugin 3.4.1 org.apache.maven.plugins maven-checkstyle-plugin 3.2.1 com.puppycrawl.tools checkstyle 10.6.0 org.codehaus.mojo animal-sniffer-maven-plugin 1.22 org.codehaus.mojo versions-maven-plugin 2.14.2 org.apache.maven.plugins maven-install-plugin 3.0.1 org.apache.maven.plugins maven-deploy-plugin 3.0.0-M2 org.apache.maven.plugins maven-gpg-plugin 3.0.1 org.apache.maven.plugins maven-release-plugin 3.0.0-M6 org.apache.maven.plugins maven-javadoc-plugin jdk8 1.8 ${jdk.target} ${jdk.target} jdk9+ (1.8,) ${jdk.target} versions org.codehaus.mojo versions-maven-plugin dependency-updates-report plugin-updates-report property-updates-report checkstyle org.apache.maven.plugins maven-checkstyle-plugin validate check true true ${project.basedir}/src/checkstyle/checks.xml true **/guide/* sonar org.sonarsource.scanner.maven sonar-maven-plugin org.jacoco jacoco-maven-plugin prepare-agent prepare-agent report report gpgsign org.apache.maven.plugins maven-gpg-plugin sign-artifacts verify sign android org.codehaus.mojo animal-sniffer-maven-plugin net.sf.androidscents.signature:android-api-level-28:9_r6 test test check jeromq-0.6.0/scripts/000077500000000000000000000000001455771126300145145ustar00rootroot00000000000000jeromq-0.6.0/scripts/prepare-android.sh000077500000000000000000000010671455771126300201330ustar00rootroot00000000000000#!/bin/bash set -ev ANDROID_ROOT=src/android/Jeromq/app/src/ rm -fr $ANDROID_ROOT/main/java/{org,zmq} rm -fr $ANDROID_ROOT/androidTest/java/{org,zmq} cp -R src/main/java/{org,zmq} $ANDROID_ROOT/main/java cp -R src/test/java/{org,zmq} $ANDROID_ROOT/androidTest/java cp $ANDROID_ROOT/TemporaryFolderFinder.java $ANDROID_ROOT/androidTest/java/org/zeromq grep -rl zmq.util.AndroidProblematic $ANDROID_ROOT/androidTest/java | xargs -I @@ bash -c '{\ sed -i 's/zmq.util.AndroidProblematic/org.junit.Ignore/g' @@ ;\ sed -i 's/@AndroidProblematic/@Ignore/g' @@ ;\ }' jeromq-0.6.0/scripts/run-example000077500000000000000000000004621455771126300167010ustar00rootroot00000000000000#!/bin/bash if [[ $# -lt 1 ]]; then echo "Usage: $0 " fi example=$1 filename="src/test/java/guide/$example.java" if [[ ! -f $filename ]]; then echo "File not found: $filename" exit 1 fi mvn test-compile exec:java -Dexec.mainClass=guide.$example -Dexec.classpathScope=test jeromq-0.6.0/src/000077500000000000000000000000001455771126300136145ustar00rootroot00000000000000jeromq-0.6.0/src/android/000077500000000000000000000000001455771126300152345ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/000077500000000000000000000000001455771126300164715ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/.gitignore000066400000000000000000000004121455771126300204560ustar00rootroot00000000000000*.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx app/build/ app/src/main/java/* app/src/androidTest/java/* jeromq-0.6.0/src/android/Jeromq/app/000077500000000000000000000000001455771126300172515ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/app/build.gradle000066400000000000000000000022071455771126300215310ustar00rootroot00000000000000apply plugin: 'com.android.application' android { compileSdkVersion 28 buildToolsVersion "29.0.2" defaultConfig { applicationId "je.romq" minSdkVersion 14 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments notAnnotation: 'zmq.util.AndroidProblematic' } compileOptions { sourceCompatibility = 1.8 targetCompatibility = 1.8 } buildTypes { release { minifyEnabled false } } testOptions { execution 'ANDROIDX_TEST_ORCHESTRATOR' } } dependencies { implementation 'eu.neilalexander:jnacl:1.0.0' implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test:runner:1.1.0' androidTestUtil 'androidx.test:orchestrator:1.1.0' } jeromq-0.6.0/src/android/Jeromq/app/src/000077500000000000000000000000001455771126300200405ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/app/src/TemporaryFolderFinder.java000066400000000000000000000006231455771126300251520ustar00rootroot00000000000000package org.zeromq; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import java.io.File; public class TemporaryFolderFinder { public static String resolve(String file) { Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); return new File(appContext.getCacheDir(), file).getAbsolutePath(); } } jeromq-0.6.0/src/android/Jeromq/app/src/main/000077500000000000000000000000001455771126300207645ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/app/src/main/AndroidManifest.xml000066400000000000000000000006561455771126300245640ustar00rootroot00000000000000 jeromq-0.6.0/src/android/Jeromq/build.gradle000066400000000000000000000010561455771126300207520ustar00rootroot00000000000000// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.5.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } jeromq-0.6.0/src/android/Jeromq/gradle.properties000066400000000000000000000020611455771126300220440ustar00rootroot00000000000000# Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true jeromq-0.6.0/src/android/Jeromq/gradle/000077500000000000000000000000001455771126300177275ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/gradle/wrapper/000077500000000000000000000000001455771126300214075ustar00rootroot00000000000000jeromq-0.6.0/src/android/Jeromq/gradle/wrapper/gradle-wrapper.properties000066400000000000000000000003501455771126300264370ustar00rootroot00000000000000#Mon Nov 11 21:30:03 CET 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip jeromq-0.6.0/src/android/Jeromq/gradlew000077500000000000000000000122601455771126300200450ustar00rootroot00000000000000#!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" jeromq-0.6.0/src/android/Jeromq/gradlew.bat000066400000000000000000000042001455771126300206020ustar00rootroot00000000000000@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega jeromq-0.6.0/src/android/Jeromq/lint.xml000066400000000000000000000004071455771126300201620ustar00rootroot00000000000000 jeromq-0.6.0/src/android/Jeromq/settings.gradle000066400000000000000000000000511455771126300215050ustar00rootroot00000000000000include ':app' rootProject.name='Jeromq' jeromq-0.6.0/src/checkstyle/000077500000000000000000000000001455771126300157525ustar00rootroot00000000000000jeromq-0.6.0/src/checkstyle/checks.xml000066400000000000000000000120111455771126300177270ustar00rootroot00000000000000 jeromq-0.6.0/src/checkstyle/eclipse-format-profile.xml000066400000000000000000001005111455771126300230420ustar00rootroot00000000000000 jeromq-0.6.0/src/main/000077500000000000000000000000001455771126300145405ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/000077500000000000000000000000001455771126300154615ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/000077500000000000000000000000001455771126300162505ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/zeromq/000077500000000000000000000000001455771126300175655ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/zeromq/ManagedContext.java000066400000000000000000000040211455771126300233260ustar00rootroot00000000000000package org.zeromq; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import zmq.Ctx; import zmq.SocketBase; import zmq.ZMQ; // This is to avoid people trying to initialize a Context class ManagedContext { static { // Release ManagedSocket resources when catching SIGINT Runtime.getRuntime().addShutdownHook(new Thread(() -> getInstance().close())); } private final Lock lock; private final Ctx ctx; private final Set sockets; private ManagedContext() { this.ctx = ZMQ.init(ZMQ.ZMQ_IO_THREADS_DFLT); this.lock = new ReentrantLock(); this.sockets = new HashSet<>(); } static ManagedContext getInstance() { return ContextHolder.INSTANCE; } SocketBase createSocket(int type) { final SocketBase base = ctx.createSocket(type); lock.lock(); try { sockets.add(base); } finally { lock.unlock(); } return base; } void destroy(SocketBase socketBase) { socketBase.setSocketOpt(ZMQ.ZMQ_LINGER, 0); socketBase.close(); lock.lock(); try { sockets.remove(socketBase); } finally { lock.unlock(); } } /* * This should only be called when SIGINT is received */ private void close() { lock.lock(); try { for (SocketBase s : sockets) { try { s.setSocketOpt(ZMQ.ZMQ_LINGER, 0); s.close(); } catch (Exception ignore) { } } sockets.clear(); } finally { lock.unlock(); } } // Lazy singleton pattern to avoid double lock checking private static class ContextHolder { private static final ManagedContext INSTANCE = new ManagedContext(); } } jeromq-0.6.0/src/main/java/org/zeromq/SocketType.java000066400000000000000000000773661455771126300225450ustar00rootroot00000000000000package org.zeromq; import zmq.util.Draft; /** * Socket Type enumeration * * @author Isa Hekmatizadeh */ @SuppressWarnings("deprecation") public enum SocketType { /** *

Flag to specify a exclusive pair of sockets.

* * A socket of type PAIR can only be connected to a single peer at any one time. *
* No message routing or filtering is performed on messages sent over a PAIR socket. *
* When a PAIR socket enters the mute state due to having reached the high water mark for the connected peer, * or if no peer is connected, then any send() operations on the socket shall block until the peer becomes available for sending; * messages are not discarded. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer socketsPAIR
DirectionBidirectional
Send/receive patternUnrestricted
Incoming routing strategyN/A
Outgoing routing strategyN/A
Action in mute stateBlock
*

* PAIR sockets are designed for inter-thread communication across the inproc transport * and do not implement functionality such as auto-reconnection. * PAIR sockets are considered experimental and may have other missing or broken aspects. */ PAIR(ZMQ.PAIR), /** *

Flag to specify a PUB socket, receiving side must be a SUB or XSUB.

* * A socket of type PUB is used by a publisher to distribute data. *
* Messages sent are distributed in a fan out fashion to all connected peers. *
* The {@link org.zeromq.ZMQ.Socket#recv()} function is not implemented for this socket type. *
* When a PUB socket enters the mute state due to having reached the high water mark for a subscriber, * then any messages that would be sent to the subscriber in question shall instead be dropped until the mute state ends. *
* The send methods shall never block for this socket type. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#SUB}, {@link org.zeromq.ZMQ#XSUB}
DirectionUnidirectional
Send/receive patternSend only
Incoming routing strategyN/A
Outgoing routing strategyFan out
Action in mute stateDrop
*/ PUB(ZMQ.PUB), /** *

Flag to specify the receiving part of the PUB or XPUB socket.

* * A socket of type SUB is used by a subscriber to subscribe to data distributed by a publisher. *
* Initially a SUB socket is not subscribed to any messages, * use the {@link org.zeromq.ZMQ.Socket#subscribe(byte[])} option to specify which messages to subscribe to. *
* The send methods are not implemented for this socket type. *

* * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#PUB}, {@link org.zeromq.ZMQ#XPUB}
DirectionUnidirectional
Send/receive patternReceive only
Incoming routing strategyFair-queued
Outgoing routing strategyN/A
*/ SUB(ZMQ.SUB), /** *

Flag to specify a REQ socket, receiving side must be a REP or ROUTER.

* * A socket of type REQ is used by a client to send requests to and receive replies from a service. *
* This socket type allows only an alternating sequence of send(request) and subsequent recv(reply) calls. *
* Each request sent is round-robined among all services, and each reply received is matched with the last issued request. *
* If no services are available, then any send operation on the socket shall block until at least one service becomes available. *
* The REQ socket shall not discard messages. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#REP}, {@link org.zeromq.ZMQ#ROUTER}
DirectionBidirectional
Send/receive patternSend, Receive, Send, Receive, ...
Incoming routing strategyLast peer
Outgoing routing strategyRound-robin
Action in mute stateBlock
*/ REQ(ZMQ.REQ), /** *

Flag to specify the receiving part of a REQ or DEALER socket.

* * A socket of type REP is used by a service to receive requests from and send replies to a client. *
* This socket type allows only an alternating sequence of recv(request) and subsequent send(reply) calls. *
* Each request received is fair-queued from among all clients, and each reply sent is routed to the client that issued the last request. *
* If the original requester does not exist any more the reply is silently discarded. *

* * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#REQ}, {@link org.zeromq.ZMQ#DEALER}
DirectionBidirectional
Send/receive patternReceive, Send, Receive, Send, ...
Incoming routing strategyFair-queued
Outgoing routing strategyLast peer
*/ REP(ZMQ.REP), /** *

Flag to specify a DEALER socket (aka XREQ).

* * DEALER is really a combined ventilator / sink * that does load-balancing on output and fair-queuing on input * with no other semantics. It is the only socket type that lets * you shuffle messages out to N nodes and shuffle the replies * back, in a raw bidirectional asynch pattern. *
* A socket of type DEALER is an advanced pattern used for extending request/reply sockets. *
* Each message sent is round-robined among all connected peers, and each message received is fair-queued from all connected peers. *
* When a DEALER socket enters the mute state due to having reached the high water mark for all peers, * or if there are no peers at all, then any send() operations on the socket shall block * until the mute state ends or at least one peer becomes available for sending; messages are not discarded. *
* When a DEALER socket is connected to a {@link org.zeromq.ZMQ#REP} socket each message sent must consist of * an empty message part, the delimiter, followed by one or more body parts. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#ROUTER}, {@link org.zeromq.ZMQ#REP}, {@link org.zeromq.ZMQ#DEALER}
DirectionBidirectional
Send/receive patternUnrestricted
Incoming routing strategyFair-queued
Outgoing routing strategyRound-robin
Action in mute stateBlock
*/ DEALER(ZMQ.DEALER), /** *

Flag to specify ROUTER socket (aka XREP).

* * ROUTER is the socket that creates and consumes request-reply * routing envelopes. It is the only socket type that lets you route * messages to specific connections if you know their identities. *
* A socket of type ROUTER is an advanced socket type used for extending request/reply sockets. *
* When receiving messages a ROUTER socket shall prepend a message part containing the identity * of the originating peer to the message before passing it to the application. *
* Messages received are fair-queued from among all connected peers. *
* When sending messages a ROUTER socket shall remove the first part of the message * and use it to determine the identity of the peer the message shall be routed to. * If the peer does not exist anymore the message shall be silently discarded by default, * unless {@link org.zeromq.ZMQ.Socket#setRouterMandatory(boolean)} socket option is set to true. *
* When a ROUTER socket enters the mute state due to having reached the high water mark for all peers, * then any messages sent to the socket shall be dropped until the mute state ends. *
* Likewise, any messages routed to a peer for which the individual high water mark has been reached shall also be dropped, * unless {@link org.zeromq.ZMQ.Socket#setRouterMandatory(boolean)} socket option is set to true. *
* When a {@link org.zeromq.ZMQ#REQ} socket is connected to a ROUTER socket, in addition to the identity of the originating peer * each message received shall contain an empty delimiter message part. *
* Hence, the entire structure of each received message as seen by the application becomes: one or more identity parts, * delimiter part, one or more body parts. *
* When sending replies to a REQ socket the application must include the delimiter part. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#DEALER}, {@link org.zeromq.ZMQ#REQ}, {@link org.zeromq.ZMQ#ROUTER}
DirectionBidirectional
Send/receive patternUnrestricted
Incoming routing strategyFair-queued
Outgoing routing strategySee text
Action in mute stateDrop (See text)
*/ ROUTER(ZMQ.ROUTER), /** *

Flag to specify the receiving part of a PUSH socket.

* * A socket of type ZMQ_PULL is used by a pipeline node to receive messages from upstream pipeline nodes. *
* Messages are fair-queued from among all connected upstream nodes. *
* The send() function is not implemented for this socket type. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#PUSH}
DirectionUnidirectional
Send/receive patternReceive only
Incoming routing strategyFair-queued
Outgoing routing strategyN/A
Action in mute stateBlock
*/ PULL(ZMQ.PULL), /** *

Flag to specify a PUSH socket, receiving side must be a PULL.

* * A socket of type PUSH is used by a pipeline node to send messages to downstream pipeline nodes. *
* Messages are round-robined to all connected downstream nodes. *
* The recv() function is not implemented for this socket type. *
* When a PUSH socket enters the mute state due to having reached the high water mark for all downstream nodes, * or if there are no downstream nodes at all, then any send() operations on the socket shall block until the mute state ends * or at least one downstream node becomes available for sending; messages are not discarded. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#PULL}
DirectionUnidirectional
Send/receive patternSend only
Incoming routing strategyN/A
Outgoing routing strategyRound-robin
Action in mute stateBlock
*/ PUSH(ZMQ.PUSH), /** *

Flag to specify a XPUB socket, receiving side must be a SUB or XSUB.

* * Subscriptions can be received as a message. Subscriptions start with * a '1' byte. Unsubscriptions start with a '0' byte. *
* Same as {@link org.zeromq.ZMQ#PUB} except that you can receive subscriptions from the peers in form of incoming messages. *
* Subscription message is a byte '1' (for subscriptions) or byte '0' (for unsubscriptions) followed by the subscription body. *
* Messages without a sub/unsub prefix are also received, but have no effect on subscription status. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#SUB}, {@link org.zeromq.ZMQ#XSUB}
DirectionUnidirectional
Send/receive patternSend messages, receive subscriptions
Incoming routing strategyN/A
Outgoing routing strategyFan out
Action in mute stateDrop
*/ XPUB(ZMQ.XPUB), /** *

Flag to specify the receiving part of the PUB or XPUB socket.

* * Same as {@link org.zeromq.ZMQ#SUB} except that you subscribe by sending subscription messages to the socket. *
* Subscription message is a byte '1' (for subscriptions) or byte '0' (for unsubscriptions) followed by the subscription body. *
* Messages without a sub/unsub prefix may also be sent, but have no effect on subscription status. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.ZMQ#PUB}, {@link org.zeromq.ZMQ#XPUB}
DirectionUnidirectional
Send/receive patternReceive messages, send subscriptions
Incoming routing strategyFair-queued
Outgoing routing strategyN/A
Action in mute stateDrop
*/ XSUB(ZMQ.XSUB), /** *

Flag to specify a STREAM socket.

* * A socket of type STREAM is used to send and receive TCP data from a non-ØMQ peer, when using the tcp:// transport. * A STREAM socket can act as client and/or server, sending and/or receiving TCP data asynchronously. *
* When receiving TCP data, a STREAM socket shall prepend a message part containing the identity * of the originating peer to the message before passing it to the application. *
* Messages received are fair-queued from among all connected peers. * When sending TCP data, a STREAM socket shall remove the first part of the message * and use it to determine the identity of the peer the message shall be routed to, * and unroutable messages shall cause an EHOSTUNREACH or EAGAIN error. *
* To open a connection to a server, use the {@link org.zeromq.ZMQ.Socket#connect(String)} call, and then fetch the socket identity using the {@link org.zeromq.ZMQ.Socket#getIdentity()} call. * To close a specific connection, send the identity frame followed by a zero-length message. * When a connection is made, a zero-length message will be received by the application. * Similarly, when the peer disconnects (or the connection is lost), a zero-length message will be received by the application. * The {@link org.zeromq.ZMQ#SNDMORE} flag is ignored on data frames. You must send one identity frame followed by one data frame. *
* Also, please note that omitting the SNDMORE flag will prevent sending further data (from any client) on the same socket. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer socketsnone
DirectionBidirectional
Send/receive patternUnrestricted
Incoming routing strategyFair-queued
Outgoing routing strategySee text
Action in mute stateEAGAIN
*/ STREAM(ZMQ.STREAM), /** *

Flag to specify CLIENT socket.

* *

The CLIENT socket type talks to one or more SERVER peers. If connected to multiple peers, it scatters sent * messages among these peers in a round-robin fashion. On reading, it reads fairly, from each peer in turn. It is * reliable, insofar as it does not drop messages in normal cases.

* *

If the CLIENT socket has established a connection, send operations will accept messages, queue them, and send * them as rapidly as the network allows. The outgoing buffer limit is defined by the high water mark for the * socket. If the outgoing buffer is full, or if there is no connected peer, send operations will block, by default. * The CLIENT socket will not drop messages.

* *

* CLIENT sockets are threadsafe. * They do not accept the ZMQ_SNDMORE option on sends not ZMQ_RCVMORE on receives. * This limits them to single part data. * The intention is to extend the API to allow scatter/gather of multi-part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#SERVER}
DirectionBidirectional
Send/receive patternUnrestricted
Outgoing routing strategyRound Robin
Incoming routing strategyFair-queued
Action in mute stateBlock
*/ CLIENT(zmq.ZMQ.ZMQ_CLIENT), /** *

* Flag to specify SERVER socket. *

*

* The SERVER socket type talks to zero or more CLIENT peers. Each outgoing message is sent to a specific peer * CLIENT. A SERVER socket can only reply to an incoming message: the CLIENT peer must always initiate a * conversation. *

*

* Each received message has a routing_id that is a 32-bit unsigned integer. To send a message to a given CLIENT * peer the application must set the peer’s routing_id on the message. *

*

* SERVER sockets are threadsafe. * They do not accept the ZMQ_SNDMORE option on sends not ZMQ_RCVMORE on receives. * This limits them to single part data. * The intention is to extend the API to allow scatter/gather of multi-part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#CLIENT}
DirectionBidirectional
Send/receive patternUnrestricted
Outgoing routing strategySee text
Incoming routing strategyFair-queued
Action in mute stateFail
*/ SERVER(zmq.ZMQ.ZMQ_SERVER), /** *

Flag to specify RADIO socket.

*

* The radio-dish pattern is used for one-to-many distribution of data from a single publisher to multiple subscribers in a fan out fashion. * Radio-dish is using groups (vs Pub-sub topics), Dish sockets can join a group and each message sent by Radio sockets belong to a group. *

*

* Groups are strings limited to 16 chars length (including null). * The intention is to increase the length to 40 chars (including null). * The encoding of groups shall be UTF8. * Groups are matched using exact matching (vs prefix matching of PubSub). *

*

A socket of type RADIO is used by a publisher to distribute data. * Each message belong to a group, a group is specified with {@link ZFrame#setGroup(String)}. * Messages are distributed to all members of a group. * The {@link ZMQ.Socket#recv(int)} function is not implemented for this socket type. *

*

When a RADIO socket enters the mute state due to having reached the high water mark for a subscriber, * then any messages that would be sent to the subscriber in question shall instead be dropped * until the mute state ends. The {@link ZMQ.Socket#send(byte[], int)} function shall never block for this socket type. *

*

* NOTE: RADIO sockets are threadsafe. * They do not accept the ZMQ_SNDMORE option on sends. * This limits them to single part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#DISH}
DirectionUnidirectional
Send/receive patternSend only
Outgoing routing strategyFan out
Incoming routing strategyN/A
Action in mute stateDrop
*

* NOTE: RADIO is still in draft phase. *

*/ @Draft RADIO(zmq.ZMQ.ZMQ_RADIO), /** *

Flag to specify DISH socket.

* *

* The radio-dish pattern is used for one-to-many distribution of data from a single publisher to multiple subscribers in a fan out fashion. * Radio-dish is using groups (vs Pub-sub topics), Dish sockets can join a group and each message sent by Radio sockets belong to a group. *

*

* Groups are strings limited to 16 chars length (including null). * The intention is to increase the length to 40 chars (including null). * The encoding of groups shall be UTF8. * Groups are matched using exact matching (vs prefix matching of PubSub). *

* * A socket of type DISH is used by a subscriber to subscribe to groups distributed by a radio. * Initially a DISH socket is not subscribed to any groups, use {@link org.zeromq.ZMQ.Socket#join(String)} to join a group. * To get the group the message belong, call {@link ZFrame#getGroup()}. * The {@link org.zeromq.ZMQ.Socket#send(byte[], int)} function is not implemented for this socket type. * *

* NOTE: DISH sockets are threadsafe. * They do not accept ZMQ_RCVMORE on receives. * This limits them to single part data. *

* * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#RADIO}
DirectionUnidirectional
Send/receive patternReceive only
Outgoing routing strategyN/A
Incoming routing strategyFair-queued
* NOTE: DISH is still in draft phase. */ @Draft DISH(zmq.ZMQ.ZMQ_DISH), /** *

Flag to specify CHANNEL socket.

*

* The channel pattern is the thread-safe version of the exclusive pair pattern. * The channel pattern is used to connect a peer to precisely one other peer. * This pattern is used for inter-thread communication across the inproc transport. *

*

* A socket of type 'CHANNEL' can only be connected to a single peer at any one * time. No message routing or filtering is performed on messages sent over a * 'CHANNEL' socket. *

*

* When a 'CHANNEL' socket enters the 'mute' state due to having reached the * high water mark for the connected peer, or, for connection-oriented transports, * if the ZMQ_IMMEDIATE option is set and there is no connected peer, then * any {@link org.zeromq.ZMQ.Socket#send(byte[], int)} operations on the socket shall block until the peer * becomes available for sending; messages are not discarded. *

*

* While 'CHANNEL' sockets can be used over transports other than 'inproc', * their inability to auto-reconnect coupled with the fact new incoming connections will * be terminated while any previous connections (including ones in a closing state) * exist makes them unsuitable for TCP in most cases. *

*

* NOTE: 'CHANNEL' sockets are designed for inter-thread communication across * the 'inproc' transport and do not implement functionality such * as auto-reconnection. *

*

* NOTE: 'CHANNEL' sockets are threadsafe. They do not accept ZMQ_RCVMORE on receives. * This limits them to single part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer socketsCHANNEL
DirectionBidirectional
Send/receive patternUnrestricted
Outgoing routing strategyN/A
Incoming routing strategyN/A
Action in mute stateBlock
*

* NOTE: CHANNEL is still in draft phase. *

*/ @Draft CHANNEL(zmq.ZMQ.ZMQ_CHANNEL), /** *

Flag to specify PEER socket. *

*

* A 'PEER' socket talks to a set of 'PEER' sockets. *

*

* To connect and fetch the 'routing_id' of the peer use {@link ZMQ.Socket#connectPeer(String)}. *

*

* Each received message has a 'routing_id' that is a 32-bit unsigned integer. * The application can fetch this with {@link ZFrame#getRoutingId()}. *

*

* To send a message to a given 'PEER' peer the application must set the peer's * 'routing_id' on the message, using {@link ZFrame#setRoutingId(int)}. *

*

* If the 'routing_id' is not specified, or does not refer to a connected client * peer, the send call will fail with EHOSTUNREACH. If the outgoing buffer for * the peer is full, the send call shall block, unless ZMQ_DONTWAIT is * used in the send, in which case it shall fail with EAGAIN. The 'PEER' * socket shall not drop messages in any case. *

*

* NOTE: 'PEER' sockets are threadsafe. They do not accept the ZMQ_SNDMORE * option on sends not ZMQ_RCVMORE on receives. This limits them to single part * data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer socketsPEER
DirectionBidirectional
Send/receive patternUnrestricted
Outgoing routing strategySee text
Incoming routing strategyFair-queued
Action in mute stateReturn EAGAIN
* NOTE: PEER is still in draft phase. */ @Draft PEER(zmq.ZMQ.ZMQ_PEER), /** *

Flag to specify RAW socket.

* */ @Draft RAW(zmq.ZMQ.ZMQ_RAW), /** *

Flag to specify SCATTER socket. *

*

* The scatter-gather pattern is the thread-safe version of the pipeline pattern. * The scatter-gather pattern is used for distributing data to nodes arranged in a pipeline. * Data always flows down the pipeline, and each stage of the pipeline * is connected to at least one node. * When a pipeline stage is connected to multiple nodes data is round-robined among all connected nodes. *

*

* When a 'SCATTER' socket enters the 'mute' state due to having reached the * high water mark for all downstream nodes, or, for connection-oriented transports, * if the ZMQ_IMMEDIATE option is set and there are no downstream nodes at all, * then any {@link org.zeromq.ZMQ.Socket#send(byte[], int)} operations on the socket shall block until the mute * state ends or at least one downstream node becomes available for sending; * messages are not discarded. *

*

* NOTE: 'SCATTER' sockets are threadsafe. They do not accept ZMQ_RCVMORE on receives. * This limits them to single part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#GATHER}
DirectionUnidirectional
Send/receive patternSend only
Outgoing routing strategyRound-robin
Incoming routing strategyN/A
Action in mute stateBlock
* NOTE: SCATTER is still in draft phase. */ @Draft SCATTER(zmq.ZMQ.ZMQ_SCATTER), /** *

Flag to specify GATHER socket. *

*

* The scatter-gather pattern is the thread-safe version of the pipeline pattern. * The scatter-gather pattern is used for distributing data to nodes arranged in a pipeline. * Data always flows down the pipeline, and each stage of the pipeline * is connected to at least one node. * When a pipeline stage is connected to multiple nodes data is round-robined among all connected nodes. *

*

* A socket of type 'GATHER' is used by a scatter-gather node to receive messages * from upstream scatter-gather nodes. Messages are fair-queued from among all * connected upstream nodes. The {@link org.zeromq.ZMQ.Socket#send(byte[], int)} function is not implemented for * this socket type. *

*

* NOTE: 'GATHER' sockets are threadsafe. They do not accept ZMQ_RCVMORE on receives. * This limits them to single part data. *

* * * * * * * * *
Summary of socket characteristics
Compatible peer sockets{@link org.zeromq.SocketType#SCATTER}
DirectionUnidirectional
Send/receive patternReceive only
Outgoing routing strategyN/A
Incoming routing strategyFair-queued
Action in mute stateBlock
* NOTE: SCATTER is still in draft phase. */ @Draft GATHER(zmq.ZMQ.ZMQ_GATHER); public final int type; SocketType(int socketType) { this.type = socketType; } public static SocketType type(int baseType) { for (SocketType type : values()) { if (type.type == baseType) { return type; } } throw new IllegalArgumentException("no socket type found with value " + baseType); } public int type() { return type; } } jeromq-0.6.0/src/main/java/org/zeromq/UncheckedZMQException.java000066400000000000000000000007551455771126300245770ustar00rootroot00000000000000package org.zeromq; public abstract class UncheckedZMQException extends RuntimeException { private static final long serialVersionUID = 1L; public UncheckedZMQException() { super(); } public UncheckedZMQException(String message) { super(message); } public UncheckedZMQException(Throwable cause) { super(cause); } public UncheckedZMQException(String message, Throwable cause) { super(message, cause); } } jeromq-0.6.0/src/main/java/org/zeromq/Utils.java000066400000000000000000000005671455771126300215400ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; public class Utils { private Utils() { } public static int findOpenPort() throws IOException { return zmq.util.Utils.findOpenPort(); } public static void checkArgument(boolean expression, String errorMessage) { zmq.util.Utils.checkArgument(expression, errorMessage); } } jeromq-0.6.0/src/main/java/org/zeromq/ZActor.java000066400000000000000000000536511455771126300216440ustar00rootroot00000000000000package org.zeromq; import java.nio.channels.SelectableChannel; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.zeromq.ZMQ.Socket; import org.zeromq.ZPoller.EventsHandler; import zmq.util.Objects; /** *

First implementation of a background actor remotely controlled for ØMQ.

* *

This implementation is based on the {@link ZStar} one (TODO via inheritance now but this is not totally stamped and the use of the ZAgent would probably limit the collateral damages if changed) * so you should be first familiar with the metaphor there.

* *

To extensively sum up: *
* A side or endpoint designates the same thing: the thread where lives one of the two parts of the Actor system. *

* An actor has 2 sides (with a trial to use theater terms for fun (: and hopefully clarity :) *
    *
  • the Corbeille side, or control side *
    * This is where one can {@link #send(ZMsg) send} and {@link #recv() receive} control messages to the underneath actor via the ZActor and/or its agent. *
    * The ZActor lives on this side and is a way to safely communicate with the distant provided Actor *
    * Note: Corbeille is a french word designing the most privileged seats in the theater, * 1st floor, just above the orchestra (Wikipedia...). * It was the place where the King was standing during the shows, with the best seat of course! *

    * Fast users who would want to communicate with the distant {@link Actor} can use * {@link #send(ZMsg)} or {@link #recv()} from the ZActor as the ZActor is itself an agent!

  • * *
  • the Plateau side, or background side where all the performances are made by the provided actor. *
    * The provided {@link Actor} is living on the Plateau. *
    * The Plateau is made of the Stage where {@link org.zeromq.ZActor.Actor#stage(org.zeromq.ZMQ.Socket, org.zeromq.ZMQ.Socket, org.zeromq.ZPoller, int) the effective processing occurs} and of the Backstage * where the provided actor can {@link org.zeromq.ZActor.Actor#backstage(org.zeromq.ZMQ.Socket, org.zeromq.ZPoller, int) send and receive} control messages to and from the ZActor. *
    * From this side, the work is done via callbacks using the template-method pattern, applied with interfaces (?) *
  • *
*

The main purpose (or at least its intent) of this class is to clarify the contracts, roles, responsibilities and action levers related to the Plateau. *
* The provided Actor will not be alone to do the processing of each loop. *
* It will be helped by a double responsible for passing the appropriate structures at the right moment. * As a developer point of view, this double helps also to limit the complexity of the process. * However Double is an uncommon one, capturing all the lights while letting the Actor taking care of all the invisible work in the shadows. * And this is one of the points where we begin to reach the limits of the metaphor... *
* The ZActor takes care of the establishment of the background processing, calling the provided Actor * at appropriate times via its Double. It can also manage the {@link #sign() exited} state on the control side, * if using the provided {@link #send(ZMsg)} or {@link #recv()} methods. *
* It also takes care of the automatic closing of sockets and context if it had to create one. *
* An {@link Actor actor} is basically a contract interface that anyone who uses this ZActor SHALL comply to.
*

* *

TODO This interface is still a little bit tough, as instead of the 5+2 Star callbacks, here are 10! * But they allow you to have a hand on the key points of the looping, restart a new actor if needed, ... * Anyway, adapters like a {@link SimpleActor simple one} or a {@link Duo duo} are present to help you on that, reducing the amount of * methods to write.

* *

PS: Je sais qu'il y a une différence entre acteur et comédien :)

*

PPS: I know nothing about theater!

* *

Example of code for a minimalistic actor with no socket handling other than the control pipe:

* *
 * {@code
        Actor acting = new ZActor.SimpleActor()
        {
            public List createSockets(ZContext ctx, Object ... args)
            {
                assert ("TEST".equals(args[0]));
                return Arrays.asList(ctx.createSocket(ZMQ.PUB));
            }

            public boolean backstage(Socket pipe, ZPoller poller, int events)
            {
                String cmd = pipe.recvStr();
                if ("HELLO".equals(cmd)) {
                    pipe.send("WORLD");
                    // end of the actor
                    return false;
                }
                return true;
            }
        };
        ZActor actor = new ZActor(acting, "LOCK", Arrays.asList("TEST").toArray());
        Socket pipe = actor.pipe();
        boolean rc = pipe.send("HELLO");
        assert (rc);
        ZMsg msg = actor.recv();
        String world = msg.popString();
        assert ("WORLD".equals(world));
        msg = actor.recv();
        assert (msg == null);
        rc = actor.sign();
        assert (!rc);
        rc = actor.send("whatever");
        assert (!rc);
        // don't try to use the pipe
}
*/ // remote controlled background message processing API for 0MQ. public class ZActor extends ZStar // once on stage, any actor IS a star { /** * Defines the contract for the acting instance. * */ // contract interface for acting on the distant stage // TODO what can be done to reduce the amount of methods ? Split interface? public interface Actor { /** * This is the grand premiere! * Called before the creation of the first double and the sockets * 2nd in the order call of the global acting. It return the name * that will be used for the thread. If it's null, a default one * will be picked by ZActor. * * @param pipe the backstage control pipe * @return the name of the upcoming performance. */ String premiere(Socket pipe); /** * Creates and initializes sockets for the double. * This is done at each creation of a new double. * 3rd in the order call of the global acting. * 1st in the order call of the new double. * * @param ctx the context used to create sockets * @param args the arguments passed as parameters of the ZActor * @return a list of created sockets that will be managed by the double. Not null. */ List createSockets(ZContext ctx, Object... args); /** * Called when the double is started, before the first loop. * 4th in the order call of the global acting. * 2nd in the order call of the new double. * * @param pipe the backstage control pipe * @param sockets the managed sockets that were created in the previous step * @param poller the poller where to eventually register the sockets for events */ void start(Socket pipe, List sockets, ZPoller poller); /** * Called every time just before a loop starts. * 5th in the order call of the global acting. * 3nd in the order call of the new double. * 1st in the order call of the new loop. * * @param pipe the backstage control pipe * @param poller the poller of the double * @return the timeout of the coming loop. -1 to block, 0 to not wait, > 0 to wait till max the returned duration in milliseconds */ long looping(Socket pipe, ZPoller poller); /** * Called when the actor received a control message from its pipe during a loop. * 2nd in the order call of the new loop. * * @param pipe the backstage control pipe receiving the message * @param poller the poller of the double. * @param events the events source of the call * @return true in case of success, false to stop the actor. */ boolean backstage(Socket pipe, ZPoller poller, int events); /** * The actor received a message from the created sockets during a loop. * 2nd ex-aequo in the order call of the new loop. * * @param socket the socket receiving the message * @param pipe the backstage control pipe * @param poller the poller of the double. * @param events the events source of the call * @return true in case of success, false to stop the actor. */ boolean stage(Socket socket, Socket pipe, ZPoller poller, int events); /** * Called at the end of each loop. * 3rd in the order call of the new loop. * * @param pipe the backstage control pipe * @param poller the poller of the double. * @return true to continue with the current doppelganger, false to stop it. */ boolean looped(Socket pipe, ZPoller poller); /** * Called when a created socket has been closed. * The process of destroying the double has already started. * * @param socket the closed socked. */ void closed(Socket socket); /** * Called when the current double has been destroyed. * Last in the order call of the double. * * @param ctx the context. * @param pipe the backstage control pipe. * @param poller the poller of the double. * @return true to restart a new double, false to stop the acting process */ boolean destroyed(ZContext ctx, Socket pipe, ZPoller poller); /** * Called when the stage is finished. * This is the last call to the actor. * Last in the order call of the global acting. * * @param pipe the backstage control pipe * @return true to spread the word of the actor's leaving */ boolean finished(Socket pipe); } /** * Simple adapter for an actor, with no sockets, blocking calls and immediate termination. */ // simple contract implementation for acting on the stage public static class SimpleActor implements Actor { @Override public String premiere(final Socket pipe) { return null; } @Override public List createSockets(final ZContext ctx, final Object... args) { return Collections.emptyList(); } @Override public void start(final Socket pipe, final List sockets, final ZPoller poller) { // do nothing } @Override public long looping(Socket pipe, ZPoller poller) { // blocking loop return -1; } @Override public boolean backstage(final Socket pipe, final ZPoller poller, final int events) { // stop looping return false; } @Override public boolean stage(final Socket socket, final Socket pipe, final ZPoller poller, int events) { // stop looping return false; } @Override public boolean looped(final Socket pipe, final ZPoller poller) { // continue with the same double return true; } @Override public void closed(final Socket socket) { // do nothing } @Override public boolean destroyed(final ZContext ctx, final Socket pipe, final ZPoller poller) { // no restart return false; } @Override public boolean finished(final Socket pipe) { // mot de la fin if not null return true; } } /** * Another actor will be called just before the main one, * without participating to the decisions. * This is interesting as a shadowed observer of the actor's behavior. */ // contract implementation for a duo actor on the stage public static class Duo implements Actor { // the actor that will play an active role on the stage private final Actor main; // the actor that will play a passive role on the stage private final Actor shadow; public Duo(final Actor main, final Actor shadow) { super(); Objects.requireNonNull(main, "Actor shall be set to a non-null value"); Objects.requireNonNull(shadow, "Shadow Actor shall be set to a non-null value"); this.main = main; this.shadow = shadow; } @Override public String premiere(final Socket pipe) { shadow.premiere(pipe); return main.premiere(pipe); } @Override public List createSockets(final ZContext ctx, final Object... args) { shadow.createSockets(ctx, args); return main.createSockets(ctx, args); } @Override public void start(final Socket pipe, final List sockets, final ZPoller poller) { shadow.start(pipe, sockets, poller); main.start(pipe, sockets, poller); } @Override public long looping(Socket pipe, ZPoller poller) { shadow.looping(pipe, poller); return main.looping(pipe, poller); } @Override public boolean backstage(final Socket pipe, final ZPoller poller, final int events) { shadow.backstage(pipe, poller, events); return main.backstage(pipe, poller, events); } @Override public boolean stage(final Socket socket, final Socket pipe, final ZPoller poller, final int events) { shadow.stage(socket, pipe, poller, events); return main.stage(socket, pipe, poller, events); } @Override public boolean looped(final Socket pipe, final ZPoller poller) { shadow.looped(pipe, poller); return main.looped(pipe, poller); } @Override public void closed(final Socket socket) { shadow.closed(socket); main.closed(socket); } @Override public boolean destroyed(final ZContext ctx, final Socket pipe, final ZPoller poller) { shadow.destroyed(ctx, pipe, poller); return main.destroyed(ctx, pipe, poller); } @Override public boolean finished(final Socket pipe) { shadow.finished(pipe); return main.finished(pipe); } } /** * Creates a new ZActor. A new context will be created and closed at the stop of the operation. * * @param actor * the actor handling messages from either stage and backstage * @param motdelafin * the final word used to mark the end of the actor. Null to disable this mechanism. * @param args * the optional arguments that will be passed to the distant actor */ public ZActor(final Actor actor, final String motdelafin, final Object... args) { super(new ActorFortune(actor), motdelafin, args); } /** * Creates a new ZActor. * * @param selector * the creator of the selector used on the Plateau. * @param actor * the actor handling messages from either stage and backstage * @param motdelafin * the final word used to mark the end of the actor. Null to disable this mechanism. * @param args * the optional arguments that will be passed to the distant actor * @deprecated use {@link ZActor#ZActor(Actor, String, Object...)} */ @Deprecated public ZActor(final SelectorCreator selector, final Actor actor, final String motdelafin, final Object... args) { this(actor, motdelafin, args); } /** * Creates a new ZActor. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param selector * the creator of the selector used on the Plateau. * @param actor * the actor handling messages from either stage and backstage * @param motdelafin * the final word used to mark the end of the actor. Null to disable this mechanism. * @param args * the optional arguments that will be passed to the distant actor * @deprecated use {@link ZActor#ZActor(ZContext, Actor, String, Object...)} */ @Deprecated public ZActor(final ZContext context, final SelectorCreator selector, final Actor actor, final String motdelafin, final Object... args) { this(context, actor, motdelafin, args); } /** * Creates a new ZActor. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param actor * the actor handling messages from either stage and backstage * @param motdelafin * the final word used to mark the end of the actor. Null to disable this mechanism. * @param args * the optional arguments that will be passed to the distant actor */ public ZActor(final ZContext context, final Actor actor, final String motdelafin, final Object... args) { super(context, new ActorFortune(actor), motdelafin, args); } // actor creator private static final class ActorFortune implements Fortune { private final Actor actor; public ActorFortune(Actor actor) { Objects.requireNonNull(actor, "Actor shall be set to a non-null value"); this.actor = actor; } @Override public String premiere(Socket mic, Object... args) { return actor.premiere(mic); } @Override public Star create(ZContext ctx, Socket pipe, int count, Star previous, Object... args) { return new ZActor.Double(ctx, pipe, actor, args); } @Override public boolean interview(Socket mic) { return actor.finished(mic); } @Override public void party(ZContext ctx) { } } // double for the loops, easing life for the actor private static final class Double implements EventsHandler, Star { // poller used for the loop private final ZPoller poller; // control pipe for Backstage side private final Socket pipe; // managed sockets private final List sockets; // actor responsible for processing messages private final Actor actor; // context used for the closing of the sockets private final ZContext context; // creates a new double public Double(final ZContext ctx, final Socket pipe, final Actor actor, final Object... args) { this.context = ctx; this.pipe = pipe; this.actor = actor; final List created = actor.createSockets(ctx, args); assert (created != null); sockets = new ArrayList<>(created); poller = new ZPoller(ctx); poller.setGlobalHandler(this); } // before starting the loops @Override public void prepare() { poller.register(pipe, ZPoller.POLLIN); actor.start(pipe, Collections.unmodifiableList(sockets), poller); } // gives the number of events to process @Override public int breathe() { long timeout = actor.looping(pipe, poller); return poller.poll(timeout); // events have been dispatched, } // acting takes place, return true to continue till the end @Override public boolean act(int events) { // act is actually already finished for handlers return events >= 0; // Context is not shut down } // a loop just finished, return true to continue acting @Override public boolean entract() { return actor.looped(pipe, poller); } // destroys the double @Override public boolean renews() { // close every managed sockets Iterator iter = sockets.iterator(); while (iter.hasNext()) { final Socket socket = iter.next(); iter.remove(); if (socket != null) { poller.unregister(socket); socket.close(); // call back the actor to inform that a socket has been closed. actor.closed(socket); } } // let the actor decide if the stage restarts a new double return actor.destroyed(context, pipe, poller); } @Override public boolean events(SelectableChannel channel, int events) { // TODO dispatch events from channels return true; } // an event has occurred on a registered socket @Override public boolean events(final Socket socket, final int events) { if (socket != pipe) { // Process a stage message, time to play return actor.stage(socket, pipe, poller, events); } else { // Process a control message, time to update your playing return actor.backstage(pipe, poller, events); } } } } jeromq-0.6.0/src/main/java/org/zeromq/ZAgent.java000066400000000000000000000166531455771126300216330ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import java.nio.channels.Selector; import java.util.Arrays; import org.zeromq.ZMQ.Socket; /** * First implementation of an agent for a remotely controlled background service for ØMQ. * Used in conjunction with a ZStar, but not mandatory. *

* An agent is a mechanism allowing to send messages from one thread to another, and to receive messages from this other thread. *

* Its built-in implementation provides an easy communication-lock * system that will close the access once the remote thread is finished. *

* Are proposed for you a restrained set of simple but powerful messaging commands for a quick learning curve * and an access to the underlying Socket for advanced usage. */ // agent for a remote controlled background message processing API for 0MQ. // contract to be agent of a star public interface ZAgent { /** * Receives a control message sent from the Plateau in the Corbeille. * The call is blocking. * * @return the received message or null if the context was shut down. */ ZMsg recv(); /** * Receives a control message sent from the Plateau in the Corbeille. * The call times out if there is no message after the elapsed time. * * @param timeout the timeout in milliseconds before returning null. * @return the received message or null if the context was shut down or if no message after the timeout. */ ZMsg recv(int timeout); /** * Receives a control message sent from the Plateau in the Corbeille. * The call is blocking depending on the parameter. * * @param wait true to make a blocking call, false to not wait, and possibly return null * @return the received message or null if the context was shut down or if there is no message when not blocking. */ ZMsg recv(boolean wait); /** * Sends a control message from the Corbeille to the Plateau. * * @param message the message to send * @return true if the message was sent, otherwise false (if the distant Star is dead for example) */ boolean send(ZMsg message); /** * Sends a control message from Corbeille side to the Plateau side. * * @param msg the message to send * @param destroy true to destroy the message after sending it. * @return true if the message was sent, otherwise false (if the distant Star is dead for example) */ boolean send(ZMsg msg, boolean destroy); /** * Sends a control message from the Corbeille to the Plateau side. * * @param word the message to send * @return true if the message was sent, otherwise false (if the distant Star is dead for example) */ boolean send(String word); /** * Sends a control message from the Corbeille to the Plateau side. * * @param word the message to send * @param more true to send more strings in a single message * @return true if the message was sent, otherwise false (if the distant Star is dead for example) */ boolean send(String word, boolean more); /** * Gives a sign if the distant Star is here. * * @return true if here, otherwise false */ boolean sign(); /** * Returns the socket used for communication. * For advanced usage. * * @return the socket used to communicate with the distant Star. */ Socket pipe(); /** * Closes the pipe. */ void close(); class Creator { private Creator() { super(); } public static ZAgent create(Socket pipe, String lock) { return new SimpleAgent(pipe, lock); } } /** * Creates a very simple agent with an easy lock mechanism. */ final class SimpleAgent implements ZAgent { // the pipe used for communicating with the star private final Socket pipe; // the key used to lock the agent. private final byte[] lock; // the locked state. private boolean locked; /** * Creates a new simple agent. * * @param pipe the pipe used to send control messages to the distant IStar. * @param lock the lock to use. If null, the locking mechanism is omitted. */ public SimpleAgent(Socket pipe, String lock) { this.pipe = pipe; this.lock = lock == null ? null : lock.getBytes(ZMQ.CHARSET); } @Override public boolean sign() { return !locked; } @Override public void close() { locked = true; pipe.close(); } @Override public ZMsg recv() { return recv(true); } @Override public ZMsg recv(int timeout) { final int old = pipe.getReceiveTimeOut(); pipe.setReceiveTimeOut(timeout); ZMsg msg = recv(true); pipe.setReceiveTimeOut(old); return msg; } @Override public ZMsg recv(boolean wait) { if (locked) { return null; } try { ZMsg msg = ZMsg.recvMsg(pipe, wait); if (msg == null) { return null; } if (msg.size() == 1) { final ZFrame frame = msg.peek(); byte[] key = frame.getData(); if (lock != null && Arrays.equals(lock, key)) { locked = true; // this is the last message anyway, and not a one for a public display msg = null; pipe.close(); } } return msg; } catch (ZMQException e) { locked = true; return null; } } @Override public boolean send(ZMsg message) { if (locked) { return false; } return message.send(pipe); } @Override public boolean send(String word) { if (locked) { return false; } return pipe.send(word); } @Override public boolean send(String word, boolean more) { if (locked) { return false; } return pipe.send(word, more ? ZMQ.SNDMORE : 0); } @Override public boolean send(ZMsg msg, boolean destroy) { if (locked) { return false; } return msg.send(pipe, destroy); } @Override public Socket pipe() { return pipe; } } /** * Creates a selector and destroys it. * @deprecated */ // Contract for selector creation. // will be called in backstage side. @Deprecated interface SelectorCreator { /** * Creates and opens a selector. * * @return the opened selector. * @throws IOException */ Selector create() throws IOException; /** * Destroys the previously opened selector. * * @param selector the selector to close * @throws IOException */ void destroy(Selector selector) throws IOException; } } jeromq-0.6.0/src/main/java/org/zeromq/ZAuth.java000066400000000000000000000710711455771126300214710ustar00rootroot00000000000000package org.zeromq; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMQ.Socket.Mechanism; import org.zeromq.util.ZMetadata; import zmq.util.Objects; /** * A ZAuth actor takes over authentication for all incoming connections in * its context. You can whitelist or blacklist peers based on IP address, * and define policies for securing PLAIN, CURVE, and GSSAPI connections. *
* Note that libzmq provides four levels of security: default NULL (which ZAuth * does not see), and authenticated NULL, PLAIN, and CURVE, which ZAuth can see. *
* Based on zauth.c in czmq */ public class ZAuth implements Closeable { public interface Auth { /** * Configures with ad-hoc message. * @param msg the configuration message. * @param verbose true if the actor is verbose. * @return true if correctly configured, otherwise false. */ boolean configure(ZMsg msg, boolean verbose); /** * Callback for authorizing a connection. * @param request * @param verbose * @return true if the connection is authorized, false otherwise. */ boolean authorize(ZapRequest request, boolean verbose); } public static class SimplePlainAuth implements Auth { private final Properties passwords = new Properties(); // PLAIN passwords, if loaded private File passwordsFile; private long passwordsModified; @Override public boolean configure(ZMsg msg, boolean verbose) { assert (msg.size() == 2); // For now we don't do anything with domains @SuppressWarnings("unused") String domain = msg.popString(); // Get password file and load into HashMap // If the file doesn't exist we'll get an empty map String filename = msg.popString(); passwordsFile = new File(filename); if (verbose) { System.out.printf( "ZAuth: activated plain-mechanism with password-file: %s%n", passwordsFile.getAbsolutePath()); } loadPasswords(true); return true; } @Override public boolean authorize(ZapRequest request, boolean verbose) { // assert (request.username != null); // Refresh the passwords map if the file changed loadPasswords(false); String password = passwords.getProperty(request.username); if (password != null && password.equals(request.password)) { if (verbose) { System.out.printf("ZAuth: Allowed (PLAIN) username=%s\n", request.username); } request.userId = request.username; return true; } else { if (verbose) { System.out.printf("ZAuth: Denied (PLAIN) username=%s\n", request.username); } return false; } } private void loadPasswords(boolean initial) { if (!initial) { final long lastModified = passwordsFile.lastModified(); final long age = System.currentTimeMillis() - lastModified; if (lastModified > passwordsModified && age > 1000) { // File has been modified and is stable, clear map passwords.clear(); } else { return; } } passwordsModified = passwordsFile.lastModified(); try (Reader br = new BufferedReader(new FileReader(passwordsFile))) { passwords.load(br); } catch (IOException | IllegalArgumentException ex) { // Ignore the exception, just don't read the file } } } public static class SimpleCurveAuth implements Auth { private final ZCertStore.Fingerprinter fingerprinter; private ZCertStore certStore = null; private boolean allowAny; public SimpleCurveAuth() { this(new ZCertStore.Hasher()); } public SimpleCurveAuth(ZCertStore.Fingerprinter fingerprinter) { this.fingerprinter = fingerprinter; } @Override public boolean configure(ZMsg configuration, boolean verbose) { // If location is CURVE_ALLOW_ANY, allow all clients. Otherwise // treat location as a directory that holds the certificates. final String location = configuration.popString(); allowAny = location.equals(CURVE_ALLOW_ANY); if (allowAny) { if (verbose) { System.out.println("ZAuth: Allowing all clients"); } } else { if (verbose) { System.out.printf("ZAuth: Using %s as certificates directory%n", location); } certStore = new ZCertStore(location, fingerprinter); } return true; } @Override public boolean authorize(ZapRequest request, boolean verbose) { if (allowAny) { if (verbose) { System.out.println("ZAuth: allowed (CURVE allow any client)"); } return true; } else { if (certStore != null) { if (certStore.containsPublicKey(request.clientKey)) { // login allowed if (verbose) { System.out.printf("ZAuth: Allowed (CURVE) client_key=%s\n", request.clientKey); } request.userId = request.clientKey; request.metadata = certStore.getMetadata(request.clientKey); return true; } else { // login not allowed. couldn't find certificate if (verbose) { System.out.printf("ZAuth: Denied (CURVE) client_key=%s\n", request.clientKey); } return false; } } } return false; } } public static class SimpleNullAuth implements Auth { @Override public boolean configure(ZMsg configuration, boolean verbose) { return true; } @Override public boolean authorize(ZapRequest request, boolean verbose) { return true; } } private static final String ZAP_VERSION = "1.0"; public static class ZapReply { public final String version; // Version number, must be "1.0" public final String sequence; // Sequence number of request public final int statusCode; // numeric status code public final String statusText; // readable status public final String userId; // User-Id public final ZMetadata metadata; // optional metadata public final String address; // not part of the ZAP protocol, but handy information for user public final String identity; // not part of the ZAP protocol, but handy information for user private ZapReply(String version, String sequence, int statusCode, String statusText, String userId, ZMetadata metadata) { this(version, sequence, statusCode, statusText, userId, metadata, null, null); } private ZapReply(String version, String sequence, int statusCode, String statusText, String userId, ZMetadata metadata, String address, String identity) { assert (ZAP_VERSION.equals(version)); this.version = version; this.sequence = sequence; this.statusCode = statusCode; this.statusText = statusText; this.userId = userId; this.metadata = metadata; this.address = address; this.identity = identity; } private ZMsg msg() { ZMsg msg = new ZMsg(); msg.add(version); msg.add(sequence); msg.add(Integer.toString(statusCode)); msg.add(statusText); msg.add(userId == null ? "" : userId); msg.add(metadata == null ? new byte[0] : metadata.bytes()); return msg; } @Override public String toString() { return "ZapReply [" + (version != null ? "version=" + version + ", " : "") + (sequence != null ? "sequence=" + sequence + ", " : "") + "statusCode=" + statusCode + ", " + (statusText != null ? "statusText=" + statusText + ", " : "") + (userId != null ? "userId=" + userId + ", " : "") + (metadata != null ? "metadata=" + metadata : "") + "]"; } private static ZapReply recv(ZAgent agent, boolean wait) { return received(agent.recv(wait)); } private static ZapReply recv(ZAgent agent, int timeout) { return received(agent.recv(timeout)); } private static ZapReply received(ZMsg msg) { if (msg == null) { return null; } assert (msg.size() == 8); String version = msg.popString(); String sequence = msg.popString(); int statusCode = Integer.parseInt(msg.popString()); String statusText = msg.popString(); String userId = msg.popString(); ZMetadata metadata = ZMetadata.read(msg.popString()); String address = msg.popString(); String identity = msg.popString(); return new ZapReply(version, sequence, statusCode, statusText, userId, metadata, address, identity); } } /** * A small class for working with ZAP requests and replies. */ public static class ZapRequest { private final Socket handler; // socket we're talking to public final String version; // Version number, must be "1.0" public final String sequence; // Sequence number of request public final String domain; // Server socket domain public final String address; // Client IP address public final String identity; // Server socket identity public final String mechanism; // Security mechanism public final String username; // PLAIN user name public final String password; // PLAIN password, in clear text public final String clientKey; // CURVE client public key in ASCII public final String principal; // GSSAPI principal public String userId; // User-Id to return in the ZAP Response public ZMetadata metadata; // metadata to eventually return private ZapRequest(Socket handler, ZMsg request) { // Store handler socket so we can send a reply easily this.handler = handler; // Get all standard frames off the handler socket version = request.popString(); sequence = request.popString(); domain = request.popString(); address = request.popString(); identity = request.popString(); mechanism = request.popString(); // If the version is wrong, we're linked with a bogus libzmq, so die assert (ZAP_VERSION.equals(version)); // Get mechanism-specific frames if (Mechanism.PLAIN.name().equals(mechanism)) { username = request.popString(); password = request.popString(); clientKey = null; principal = null; } else if (Mechanism.CURVE.name().equals(mechanism)) { ZFrame frame = request.pop(); byte[] clientPublicKey = frame.getData(); username = null; password = null; clientKey = ZMQ.Curve.z85Encode(clientPublicKey); principal = null; } else if (zmq.io.mechanism.Mechanisms.GSSAPI.name().equals(mechanism)) { // TOD handle GSSAPI as well username = null; password = null; clientKey = null; principal = request.popString(); } else { username = null; password = null; clientKey = null; principal = null; } } private static ZapRequest recvRequest(Socket handler, boolean wait) { ZMsg request = ZMsg.recvMsg(handler, wait); if (request == null) { return null; } ZapRequest self = new ZapRequest(handler, request); // If the version is wrong, we're linked with a bogus libzmq, so die assert (ZAP_VERSION.equals(self.version)); request.destroy(); return self; } /** * Send a zap reply to the handler socket */ private void reply(int statusCode, String statusText, Socket replies) { ZapReply reply = new ZapReply(ZAP_VERSION, sequence, statusCode, statusText, userId, metadata); ZMsg msg = reply.msg(); boolean destroy = replies == null; msg.send(handler, destroy); if (replies != null) { // let's add other fields for convenience of listener msg.add(address); msg.add(identity); msg.send(replies); } } } public static final String CURVE_ALLOW_ANY = "*"; private static final String VERBOSE = "VERBOSE"; private static final String REPLIES = "REPLIES"; private static final String ALLOW = "ALLOW"; private static final String DENY = "DENY"; private static final String TERMINATE = "TERMINATE"; private final ZAgent agent; private final ZStar.Exit exit; private final ZAgent replies; private boolean repliesEnabled; // are replies enabled? /** * Install authentication for the specified context. Note that until you add * policies, all incoming NULL connections are allowed (classic ZeroMQ * behavior), and all PLAIN and CURVE connections are denied. * @param ctx */ public ZAuth(ZContext ctx) { this(ctx, "ZAuth"); } public ZAuth(ZContext ctx, ZCertStore.Fingerprinter fingerprinter) { this(ctx, "ZAuth", curveVariant(fingerprinter)); } public ZAuth(ZContext ctx, String actorName) { this(ctx, actorName, makeSimpleAuths()); } private static Map makeSimpleAuths() { Map auths = new HashMap<>(); auths.put(Mechanism.PLAIN.name(), new SimplePlainAuth()); auths.put(Mechanism.CURVE.name(), new SimpleCurveAuth()); auths.put(Mechanism.NULL.name(), new SimpleNullAuth()); // TODO add GSSAPI once it is implemented return auths; } private static Map curveVariant(ZCertStore.Fingerprinter fingerprinter) { Map auths = makeSimpleAuths(); auths.put(Mechanism.CURVE.name(), new SimpleCurveAuth(fingerprinter)); return auths; } public ZAuth(final ZContext ctx, String actorName, Map auths) { Objects.requireNonNull(ctx, "ZAuth works only with a provided ZContext"); Objects.requireNonNull(actorName, "Actor name shall be defined"); Objects.requireNonNull(auths, "Authenticators shall be supplied as non-null map"); final AuthActor actor = new AuthActor(actorName, auths); final ZActor zactor = new ZActor(ctx, actor, UUID.randomUUID().toString()); agent = zactor.agent(); exit = zactor.exit(); // wait for the start of the actor agent.recv().destroy(); replies = actor.createAgent(ctx); } /** * Enable verbose tracing of commands and activity * @param verbose */ public ZAuth setVerbose(boolean verbose) { return verbose(verbose); } public ZAuth verbose(boolean verbose) { return send(VERBOSE, String.format("%b", verbose)); } /** * Allow (whitelist) a single IP address. For NULL, all clients from this * address will be accepted. For PLAIN and CURVE, they will be allowed to * continue with authentication. You can call this method multiple times to * whitelist multiple IP addresses. If you whitelist a single address, any * non-whitelisted addresses are treated as blacklisted. * @param address */ public ZAuth allow(String address) { Objects.requireNonNull(address, "Address has to be supplied for allowance"); return send(ALLOW, address); } /** * Deny (blacklist) a single IP address. For all security mechanisms, this * rejects the connection without any further authentication. Use either a * whitelist, or a blacklist, not not both. If you define both a whitelist * and a blacklist, only the whitelist takes effect. * @param address */ public ZAuth deny(String address) { Objects.requireNonNull(address, "Address has to be supplied for denial"); return send(DENY, address); } /** * Configure PLAIN authentication for a given domain. PLAIN authentication * uses a plain-text password file. To cover all domains, use "*". You can * modify the password file at any time; it is reloaded automatically. * @param domain * @param filename */ public ZAuth configurePlain(String domain, String filename) { Objects.requireNonNull(domain, "Domain has to be supplied"); Objects.requireNonNull(filename, "File name has to be supplied"); return send(Mechanism.PLAIN.name(), domain, filename); } /** * Configure CURVE authentication * * @param location Can be ZAuth.CURVE_ALLOW_ANY or a directory with public-keys that will be accepted */ public ZAuth configureCurve(String location) { Objects.requireNonNull(location, "Location has to be supplied"); return send(Mechanism.CURVE.name(), location); } public ZAuth replies(boolean enable) { repliesEnabled = enable; return send(REPLIES, String.format("%b", enable)); } /** * Retrieves the next ZAP reply. * @return the next reply or null if the actor is closed. */ public ZapReply nextReply() { return nextReply(true); } /** * Retrieves the next ZAP reply. * @param wait true to wait for the next reply, false to immediately return if there is no next reply. * @return the next reply or null if the actor is closed or if there is no next reply yet. */ public ZapReply nextReply(boolean wait) { if (!repliesEnabled) { System.out.println("ZAuth: replies are disabled. Please use replies(true);"); return null; } return ZapReply.recv(replies, wait); } /** * Retrieves the next ZAP reply. * @param timeout the timeout in milliseconds to wait for a reply before giving up and returning null. * @return the next reply or null if the actor is closed or if there is no next reply after the elapsed timeout. */ public ZapReply nextReply(int timeout) { if (!repliesEnabled) { System.out.println("ZAuth: replies are disabled. Please use replies(true);"); return null; } return ZapReply.recv(replies, timeout); } /** * Destructor. */ @Override public void close() { destroy(); } /** * Destructor. */ public void destroy() { send(TERMINATE); exit.awaitSilent(); agent.close(); replies.close(); } protected ZAuth send(String command, String... datas) { ZMsg msg = new ZMsg(); msg.add(command); for (String data : datas) { msg.add(data); } agent.send(msg); msg.destroy(); agent.recv(); return this; } /** * AuthActor is the backend actor which we talk to over a pipe. This lets * the actor do work asynchronously in the background while our application * does other things. This is invisible to the caller, who sees a classic * API. */ private static class AuthActor extends ZActor.SimpleActor { private static final String OK = "OK"; private final String actorName; private final Properties whitelist = new Properties(); // whitelisted addresses private final Properties blacklist = new Properties(); // blacklisted addresses private final Map auths = new HashMap<>(); private final String repliesAddress; // address of replies pipe AND safeguard lock for connected agent private boolean repliesEnabled; // are replies enabled? private Socket replies; // replies pipe private boolean verbose; // trace behavior private AuthActor(String actorName, Map auths) { assert (auths != null); assert (actorName != null); this.actorName = actorName; this.auths.putAll(auths); this.repliesAddress = "inproc://zauth-replies-" + UUID.randomUUID(); } private ZAgent createAgent(ZContext ctx) { Socket pipe = ctx.createSocket(SocketType.PAIR); boolean rc = pipe.connect(repliesAddress); assert (rc); return new ZAgent.SimpleAgent(pipe, repliesAddress); } @Override public String premiere(Socket pipe) { return actorName; } @Override public List createSockets(ZContext ctx, Object... args) { //create replies pipe that will forward replies to user replies = ctx.createSocket(SocketType.PAIR); assert (replies != null); //create ZAP handler and get ready for requests Socket handler = ctx.createSocket(SocketType.REP); assert (handler != null); return Arrays.asList(handler, replies); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { boolean rc; try { rc = replies.bind(repliesAddress); assert (rc); Socket handler = sockets.get(0); rc = handler.bind("inproc://zeromq.zap.01"); assert (rc); rc = poller.register(handler, ZPoller.POLLIN); assert (rc); rc = pipe.send(OK); assert (rc); } catch (ZMQException e) { System.out.println("ZAuth: Error"); e.printStackTrace(); rc = pipe.send("ERROR"); assert (rc); } } @Override public boolean backstage(Socket pipe, ZPoller poller, int events) { ZMsg msg = ZMsg.recvMsg(pipe); String command = msg.popString(); if (command == null) { System.out.printf("ZAuth: Closing auth: No command%n"); return false; //interrupted } boolean rc; if (ALLOW.equals(command)) { String address = msg.popString(); if (verbose) { System.out.printf("ZAuth: Whitelisting IP address=%s\n", address); } whitelist.put(address, OK); rc = pipe.send(OK); } else if (DENY.equals(command)) { String address = msg.popString(); if (verbose) { System.out.printf("ZAuth: Blacklisting IP address=%s\n", address); } blacklist.put(address, OK); rc = pipe.send(OK); } else if (VERBOSE.equals(command)) { String verboseStr = msg.popString(); this.verbose = Boolean.parseBoolean(verboseStr); rc = pipe.send(OK); } else if (REPLIES.equals(command)) { repliesEnabled = Boolean.parseBoolean(msg.popString()); if (verbose) { if (repliesEnabled) { System.out.println("ZAuth: Enabled replies"); } else { System.out.println("ZAuth: Disabled replies"); } } rc = pipe.send(OK); } else if (TERMINATE.equals(command)) { if (repliesEnabled) { replies.send(repliesAddress); // lock replies agent } if (verbose) { System.out.println("ZAuth: Terminated"); } pipe.send(OK); return false; } else { final Auth authenticator = auths.get(command); if (authenticator != null) { if (authenticator.configure(msg, verbose)) { rc = pipe.send(OK); } else { rc = pipe.send("ERROR"); } } else { System.out.printf("ZAuth: Invalid command %s%n", command); rc = true; } } msg.destroy(); if (!rc) { System.out.printf("ZAuth: Command in error %s%n", command); } return rc; } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { ZapRequest request = ZapRequest.recvRequest(socket, true); if (request == null) { return false; } //is the address explicitly whitelisted or blacklisted? boolean allowed = false; boolean denied = false; if (!whitelist.isEmpty()) { if (whitelist.containsKey(request.address)) { allowed = true; if (verbose) { System.out.printf("ZAuth: Passed (whitelist) address = %s\n", request.address); } } else { denied = true; if (verbose) { System.out.printf("ZAuth: Denied (not in whitelist) address = %s\n", request.address); } } } else if (!blacklist.isEmpty()) { if (blacklist.containsKey(request.address)) { denied = true; if (verbose) { System.out.printf("ZAuth: Denied (blacklist) address = %s\n", request.address); } } else { allowed = true; if (verbose) { System.out.printf("ZAuth: Passed (not in blacklist) address = %s\n", request.address); } } } //mechanism specific check if (!denied) { final Auth auth = auths.get(request.mechanism); if (auth == null) { System.out.printf("ZAuth E: Skipping unhandled mechanism %s%n", request.mechanism); return false; } else { allowed = auth.authorize(request, verbose); } } final Socket reply = repliesEnabled ? replies : null; if (allowed) { request.reply(200, OK, reply); } else { request.metadata = null; request.reply(400, "NO ACCESS", reply); } return true; } } } jeromq-0.6.0/src/main/java/org/zeromq/ZBeacon.java000066400000000000000000000412721455771126300217570ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import zmq.util.Objects; /** * This class implements a peer-to-peer discovery service for local networks. * A beacon can broadcast and/or capture service announcements using UDP messages * on the local area network. This implementation uses IPv4 UDP broadcasts. You can * define the format of your outgoing beacons, and set a filter that validates incoming * beacons. Beacons are sent and received asynchronously in the background. * */ public class ZBeacon { public static final long DEFAULT_BROADCAST_INTERVAL = 1000L; public static final String DEFAULT_BROADCAST_HOST = "255.255.255.255"; // is this the source/interface address? or the broadcast address private static final InetAddress DEFAULT_BROADCAST_HOST_ADDRESS; private static final InetAddress DEFAULT_BROADCAST_ADDRESS; static { try { DEFAULT_BROADCAST_HOST_ADDRESS = InetAddress.getByName(DEFAULT_BROADCAST_HOST); DEFAULT_BROADCAST_ADDRESS = InetAddress.getByName("0.0.0.0"); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid default broadcast address", e); } } private final BroadcastClient broadcastClient; private final BroadcastServer broadcastServer; private final AtomicReference prefix = new AtomicReference<>(); private final AtomicReference beacon = new AtomicReference<>(); private final AtomicLong broadcastInterval = new AtomicLong(DEFAULT_BROADCAST_INTERVAL); private final AtomicReference listener = new AtomicReference<>(); private final AtomicReference clientExHandler = new AtomicReference<>(); private final AtomicReference serverExHandler = new AtomicReference<>(); public ZBeacon(int port, byte[] beacon) { this(DEFAULT_BROADCAST_HOST, port, beacon); } public ZBeacon(String host, int port, byte[] beacon) { this(host, port, beacon, true); } public ZBeacon(String host, int port, byte[] beacon, boolean ignoreLocalAddress) { this(host, port, beacon, ignoreLocalAddress, false); } public ZBeacon(String host, int port, byte[] beacon, boolean ignoreLocalAddress, boolean blocking) { this(host, DEFAULT_BROADCAST_ADDRESS.getAddress(), port, beacon, DEFAULT_BROADCAST_INTERVAL, ignoreLocalAddress, blocking); } public ZBeacon(InetAddress broadcastAddress, InetAddress serverAddress, int port, byte[] beacon, long broadcastInterval, boolean ignoreLocalAddress, boolean blocking) { Objects.requireNonNull(broadcastAddress, "Host cannot be null"); Objects.requireNonNull(serverAddress, "Server address cannot be null"); Objects.requireNonNull(beacon, "Beacon cannot be null"); this.broadcastInterval.set(broadcastInterval); this.beacon.set(Arrays.copyOf(beacon, beacon.length)); broadcastServer = new BroadcastServer(port, ignoreLocalAddress); broadcastClient = new BroadcastClient(serverAddress, broadcastAddress, port, this.broadcastInterval); } @Deprecated public ZBeacon(String broadcastAddress, byte[] serverAddress, int port, byte[] beacon, long broadcastInterval, boolean ignoreLocalAddress, boolean blocking) { Objects.requireNonNull(broadcastAddress, "Host cannot be null"); Objects.requireNonNull(serverAddress, "Server address cannot be null"); Objects.requireNonNull(beacon, "Beacon cannot be null"); this.broadcastInterval.set(broadcastInterval); this.beacon.set(Arrays.copyOf(beacon, beacon.length)); broadcastServer = new BroadcastServer(port, ignoreLocalAddress); try { broadcastClient = new BroadcastClient(InetAddress.getByAddress(serverAddress), InetAddress.getByName(broadcastAddress), port, this.broadcastInterval); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid server address", e); } } public static class Builder { private InetAddress clientHost = DEFAULT_BROADCAST_HOST_ADDRESS; private InetAddress serverAddr = DEFAULT_BROADCAST_ADDRESS; private int port; private long broadcastInterval = DEFAULT_BROADCAST_INTERVAL; private byte[] beacon; private boolean ignoreLocalAddress = true; private boolean blocking = false; // Those arguments are not set using the constructor, so they are optional for backward compatibility private Listener listener = null; private byte[] prefix = null; private Thread.UncaughtExceptionHandler clientExHandler = null; private Thread.UncaughtExceptionHandler serverExHandler = null; public Builder port(int port) { this.port = port; return this; } public Builder beacon(byte[] beacon) { this.beacon = beacon; return this; } @Deprecated public Builder client(String host) { try { this.clientHost = InetAddress.getByName(host); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid server address", e); } return this; } public Builder client(InetAddress host) { this.clientHost = host; return this; } @Deprecated public Builder server(byte[] addr) { Utils.checkArgument(addr.length == 4 || addr.length == 16, "Server Address has to be 4 or 16 bytes long"); try { this.serverAddr = InetAddress.getByAddress(addr); } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid server address", e); } return this; } public Builder server(InetAddress addr) { this.serverAddr = addr; return this; } public Builder ignoreLocalAddress(boolean ignoreLocalAddress) { this.ignoreLocalAddress = ignoreLocalAddress; return this; } /** * @deprecated ignored * @param blocking * @return */ @Deprecated public Builder blocking(boolean blocking) { this.blocking = blocking; return this; } public Builder broadcastInterval(long broadcastInterval) { this.broadcastInterval = broadcastInterval; return this; } public Builder listener(Listener listener) { this.listener = listener; return this; } public Builder prefix(byte[] prefix) { this.prefix = Arrays.copyOf(prefix, prefix.length); return this; } public Builder setClientUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler clientExHandler) { this.clientExHandler = clientExHandler; return this; } public Builder setServerUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler serverExHandler) { this.serverExHandler = serverExHandler; return this; } public ZBeacon build() { ZBeacon zbeacon = new ZBeacon(clientHost, serverAddr, port, beacon, broadcastInterval, ignoreLocalAddress, blocking); if (listener != null) { zbeacon.setListener(listener); } if (prefix != null) { zbeacon.setPrefix(prefix); } if (this.serverExHandler != null) { zbeacon.serverExHandler.set(this.serverExHandler); } if (this.clientExHandler != null) { zbeacon.clientExHandler.set(this.clientExHandler); } return zbeacon; } } /** * @deprecated use the builder * @param clientExHandler * @param serverExHandler */ @Deprecated public void setUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler clientExHandler, Thread.UncaughtExceptionHandler serverExHandler) { this.clientExHandler.set(clientExHandler); this.serverExHandler.set(serverExHandler); } public void startClient() { if (!broadcastClient.isRunning) { if (broadcastClient.thread == null) { broadcastClient.thread = new Thread(broadcastClient); broadcastClient.thread.setName("ZBeacon Client Thread"); broadcastClient.thread.setDaemon(true); broadcastClient.thread.setUncaughtExceptionHandler(clientExHandler.get()); } broadcastClient.thread.start(); } } public void startServer() { if (!broadcastServer.isRunning) { if (listener.get() != null) { if (broadcastServer.thread == null) { broadcastServer.thread = new Thread(broadcastServer); broadcastServer.thread.setName("ZBeacon Server Thread"); broadcastServer.thread.setDaemon(true); broadcastServer.thread.setUncaughtExceptionHandler(serverExHandler.get()); } broadcastServer.thread.start(); } } } public void start() { startClient(); startServer(); } public void stop() throws InterruptedException { if (broadcastClient.thread != null) { broadcastClient.thread.interrupt(); broadcastClient.thread.join(); } if (broadcastServer.thread != null) { broadcastServer.thread.interrupt(); broadcastServer.thread.join(); } } /** * @deprecated use the builder * @param beacon */ @Deprecated public void setBeacon(byte[] beacon) { this.beacon.set(Arrays.copyOf(beacon, beacon.length)); } public byte[] getBeacon() { byte[] beaconBuffer = beacon.get(); return Arrays.copyOf(beaconBuffer, beaconBuffer.length); } /** * @deprecated use the builder * @param prefix */ @Deprecated public void setPrefix(byte[] prefix) { this.prefix.set(Arrays.copyOf(prefix, prefix.length)); } public byte[] getPrefix() { byte[] prefixBuffer = prefix.get(); return Arrays.copyOf(prefixBuffer, prefixBuffer.length); } /** * @deprecated use the builder * @param listener */ @Deprecated public void setListener(Listener listener) { this.listener.set(listener); } public Listener getListener() { return listener.get(); } /** * All beacons with matching prefix are passed to a listener. */ public interface Listener { void onBeacon(InetAddress sender, byte[] beacon); } /** * The broadcast client periodically sends beacons via UDP to the network. */ private class BroadcastClient implements Runnable { private final InetSocketAddress broadcastAddress; private final InetAddress interfaceAddress; private final AtomicLong broadcastInterval; private boolean isRunning; private Thread thread; public BroadcastClient(InetAddress interfaceAddress, InetAddress broadcastAddress, int port, AtomicLong broadcastInterval) { this.broadcastInterval = broadcastInterval; this.broadcastAddress = new InetSocketAddress(broadcastAddress, port); this.interfaceAddress = interfaceAddress; } @Override public void run() { try (DatagramChannel broadcastChannel = DatagramChannel.open()) { broadcastChannel.socket().setBroadcast(true); broadcastChannel.socket().setReuseAddress(true); broadcastChannel.socket().bind(new InetSocketAddress(interfaceAddress, 0)); broadcastChannel.connect(broadcastAddress); isRunning = true; while (!Thread.interrupted() && isRunning) { try { broadcastChannel.send(ByteBuffer.wrap(beacon.get()), broadcastAddress); Thread.sleep(broadcastInterval.get()); } catch (InterruptedException | ClosedByInterruptException interruptedException) { // Re-interrupt the thread so the caller can handle it. Thread.currentThread().interrupt(); break; } catch (Exception exception) { throw new RuntimeException(exception); } } } catch (IOException ioException) { throw new RuntimeException(ioException); } finally { isRunning = false; thread = null; } } } /** * The broadcast server receives beacons. */ private class BroadcastServer implements Runnable { private final DatagramChannel handle; // Socket for send/recv private final boolean ignoreLocalAddress; private Thread thread; private boolean isRunning; public BroadcastServer(int port, boolean ignoreLocalAddress) { this.ignoreLocalAddress = ignoreLocalAddress; try { // Create UDP socket handle = DatagramChannel.open(); handle.configureBlocking(true); handle.socket().setReuseAddress(true); handle.socket().bind(new InetSocketAddress(port)); } catch (IOException ioException) { throw new RuntimeException(ioException); } } @Override public void run() { ByteBuffer buffer = ByteBuffer.allocate(65535); SocketAddress sender; isRunning = true; try { while (!Thread.interrupted() && isRunning) { buffer.clear(); try { sender = handle.receive(buffer); InetAddress senderAddress = ((InetSocketAddress) sender).getAddress(); if (ignoreLocalAddress && (InetAddress.getLocalHost().getHostAddress().equals(senderAddress.getHostAddress()) || senderAddress.isAnyLocalAddress() || senderAddress.isLoopbackAddress())) { continue; } handleMessage(buffer, senderAddress); } catch (ClosedChannelException ioException) { break; } catch (IOException ioException) { isRunning = false; throw new RuntimeException(ioException); } } } finally { handle.socket().close(); isRunning = false; thread = null; } } private void handleMessage(ByteBuffer buffer, InetAddress from) { byte[] prefix = ZBeacon.this.prefix.get(); if (buffer.remaining() < prefix.length) { return; } buffer.flip(); buffer.mark(); byte[] prefixTry = new byte[prefix.length]; buffer.get(prefixTry); if (Arrays.equals(prefix, prefixTry)) { buffer.reset(); byte[] content = new byte[buffer.remaining()]; buffer.get(content); listener.get().onBeacon(from, content); } } } public long getBroadcastInterval() { return broadcastInterval.get(); } public void setBroadcastInterval(long broadcastInterval) { this.broadcastInterval.set(broadcastInterval); } } jeromq-0.6.0/src/main/java/org/zeromq/ZCert.java000066400000000000000000000160621455771126300214640ustar00rootroot00000000000000package org.zeromq; import java.io.File; import java.io.IOException; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.Locale; import java.util.Map; import org.zeromq.ZMQ.Curve; import org.zeromq.ZMQ.Curve.KeyPair; import org.zeromq.util.ZMetadata; /** * The ZCert class provides a way to create and work with security certificates for the ZMQ CURVE mechanism. A certificate contains a public + secret key pair, plus metadata. It can be used as a temporary object in memory, or persisted to disk.

To exchange certificates, send the public file via some secure route. Certificates are not signed but are text files that can be verified by eye.

Certificates are stored in the ZeroMQ Property Language format.
They have two sections, "metadata" and "curve".
The first contains a list of 'name = value' pairs, one per line. Values may be enclosed in quotes.
The curve section has a 'public-key = key-value' and, for secret certificates, a 'secret-key = key-value' line.
The key-value is a {@link zmq.util.Z85 Z85-encoded CURVE key}. * */ public class ZCert { private final byte[] publicKey; // Public key in binary private final byte[] secretKey; // Secret key in binary private final String publicTxt; // Public key in Z85 text private final String secretTxt; // Secret key in Z85 text private final ZMetadata metadata = new ZMetadata(); // Certificate metadata public ZCert() { this(ZMQ.Curve.generateKeyPair()); } public ZCert(String publicKey) { this(publicKey, null); } public ZCert(KeyPair keypair) { this(keypair.publicKey, keypair.secretKey); } public ZCert(byte[] publicKey, byte[] secretKey) { Utils.checkArgument(publicKey != null, "Public key has to be provided for a ZCert"); assertKey(publicKey.length, Curve.KEY_SIZE, "Public"); if (secretKey != null) { assertKey(secretKey.length, Curve.KEY_SIZE, "Secret"); } this.publicKey = Arrays.copyOf(publicKey, publicKey.length); this.publicTxt = Curve.z85Encode(this.publicKey); if (secretKey == null) { this.secretKey = null; this.secretTxt = null; } else { this.secretKey = Arrays.copyOf(secretKey, secretKey.length); this.secretTxt = Curve.z85Encode(this.secretKey); } } public ZCert(String publicKey, String secretKey) { Utils.checkArgument(publicKey != null, "Public key has to be provided for a ZCert"); assertKey(publicKey.length(), Curve.KEY_SIZE_Z85, "Public"); if (secretKey != null) { assertKey(secretKey.length(), Curve.KEY_SIZE_Z85, "Secret"); } this.publicKey = Curve.z85Decode(publicKey); this.publicTxt = publicKey; if (secretKey == null) { this.secretKey = null; this.secretTxt = null; } else { this.secretKey = Curve.z85Decode(secretKey); this.secretTxt = secretKey; } } private void assertKey(int length, int expected, String flavour) { Utils.checkArgument(length == expected, flavour + " key shall have a size of " + expected); } public byte[] getPublicKey() { return publicKey; } public byte[] getSecretKey() { return secretKey; } public String getPublicKeyAsZ85() { return publicTxt; } public String getSecretKeyAsZ85() { return secretTxt; } public void apply(ZMQ.Socket socket) { socket.setCurvePublicKey(publicKey); socket.setCurveSecretKey(secretKey); } public ZMetadata getMetadata() { return metadata; } public void setMeta(String key, String value) { metadata.set(key, value); } public void unsetMeta(String key) { metadata.remove(key); } public String getMeta(String key) { return metadata.get(key); } private void add(ZMetadata meta, ZConfig config) { String now = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH).format(new Date()); config.addComment(String.format("** Generated on %1$s by ZCert **", now)); for (Map.Entry e : meta.entrySet()) { config.putValue("metadata/" + e.getKey(), e.getValue()); } } /** * Saves the public key to a file. *

* This method will overwrite contents of existing file * @param filename the path of the file to save the certificate into. * @return the saved file or null if dumped to the standard output * @throws IOException if unable to save the file. */ public File savePublic(String filename) throws IOException { return publicConfig().save(filename); } /** * Saves the public key to a writer. * @param writer the writer to save the certificate into. * @throws IOException if unable to dump the public configuration. */ public void savePublic(Writer writer) throws IOException { publicConfig().save(writer); } private ZConfig publicConfig() { ZConfig conf = new ZConfig("root", null); add(metadata, conf); conf.addComment(" ZeroMQ CURVE Public Certificate"); conf.addComment(" Exchange securely, or use a secure mechanism to verify the contents"); conf.addComment(" of this file after exchange. Store public certificates in your home"); conf.addComment(" directory, in the .curve subdirectory."); conf.putValue("/curve/public-key", publicTxt); return conf; } /** * Saves the public and secret keys to a file. *

* This method will overwrite contents of existing file * @param filename the path of the file to save the certificate into. * @return the saved file or null if dumped to the standard output * @throws IOException if unable to save the file. */ public File saveSecret(String filename) throws IOException { return secretConfig().save(filename); } /** * Saves the public and secret keys to a writer. * @param writer the writer to save the certificate into. * @throws IOException if unable to dump the configuration. */ public void saveSecret(Writer writer) throws IOException { secretConfig().save(writer); } private ZConfig secretConfig() { ZConfig conf = new ZConfig("root", null); add(metadata, conf); conf.addComment(" ZeroMQ CURVE **Secret** Certificate"); conf.addComment(" DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions."); conf.putValue("/curve/public-key", publicTxt); conf.putValue("/curve/secret-key", secretTxt); return conf; } } jeromq-0.6.0/src/main/java/org/zeromq/ZCertStore.java000066400000000000000000000230671455771126300225040ustar00rootroot00000000000000package org.zeromq; import org.zeromq.util.ZDigest; import org.zeromq.util.ZMetadata; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * To authenticate new clients using the ZeroMQ CURVE security mechanism, we have to check that the client's public key matches a key we know and accept. There are numerous ways to store accepted client public keys. The mechanism CZMQ implements is "certificates" (plain text files) held in a "certificate store" (a disk directory). This class works with such certificate stores, and lets you easily load them from disk, and check if a given client public key is known or not. The {@link org.zeromq.ZCert} class does the work of managing a single certificate. *

Those files need to be in ZMP-Format which is created by {@link org.zeromq.ZConfig}

*/ public class ZCertStore { public interface Fingerprinter { byte[] print(File path); } public static final class Timestamper implements Fingerprinter { @Override public byte[] print(File path) { final byte[] buf = new byte[8]; final long value = path.lastModified(); buf[0] = (byte) ((value >>> 56) & 0xff); buf[1] = (byte) ((value >>> 48) & 0xff); buf[2] = (byte) ((value >>> 40) & 0xff); buf[3] = (byte) ((value >>> 32) & 0xff); buf[4] = (byte) ((value >>> 24) & 0xff); buf[5] = (byte) ((value >>> 16) & 0xff); buf[6] = (byte) ((value >>> 8) & 0xff); buf[7] = (byte) ((value) & 0xff); return buf; } } public static final class Hasher implements Fingerprinter { // temporary buffer used for digest. Instance member for performance reasons. private final byte[] buffer = new byte[8192]; @Override public byte[] print(File path) { InputStream input = stream(path); if (input != null) { try { return new ZDigest(buffer).update(input).data(); } catch (IOException e) { return null; } finally { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } private InputStream stream(File path) { if (path.isFile()) { try { return new FileInputStream(path); } catch (FileNotFoundException e) { return null; } } else if (path.isDirectory()) { List list = Arrays.asList(path.list()); Collections.sort(list); return new ByteArrayInputStream(list.toString().getBytes(ZMQ.CHARSET)); } return null; } } private interface IFileVisitor { /** * Visits a file. * @param file the file to visit. * @return true to stop the traversal, false to continue. */ boolean visitFile(File file); /** * Visits a directory. * @param dir the directory to visit. * @return true to stop the traversal, false to continue. */ boolean visitDir(File dir); } // Directory location private final File location; // the scanned files (and directories) along with their fingerprint private final Map fingerprints = new HashMap<>(); // collected public keys private final Map publicKeys = new HashMap<>(); private final Fingerprinter finger; /** * Create a Certificate Store at that file system folder location * @param location */ public ZCertStore(String location) { this(location, new Timestamper()); } public ZCertStore(String location, Fingerprinter fingerprinter) { this.finger = fingerprinter; this.location = new File(location); loadFiles(); } private boolean traverseDirectory(File root, IFileVisitor visitor) { assert (root.exists()); assert (root.isDirectory()); if (visitor.visitDir(root)) { return true; } for (File file : root.listFiles()) { if (file.isFile()) { if (visitor.visitFile(file)) { return true; } } else if (file.isDirectory()) { boolean rc = traverseDirectory(file, visitor); if (rc) { return true; } } else { System.out.printf( "WARNING: %s is neither file nor directory? This shouldn't happen....SKIPPING%n", file.getAbsolutePath()); } } return false; } /** * Check if a public key is in the certificate store. * @param publicKey needs to be a 32 byte array representing the public key */ public boolean containsPublicKey(byte[] publicKey) { Utils.checkArgument( publicKey.length == 32, "publickey needs to have a size of 32 bytes. got only " + publicKey.length); return containsPublicKey(ZMQ.Curve.z85Encode(publicKey)); } /** * check if a z85-based public key is in the certificate store. * This method will scan the folder for changes on every call * @param publicKey */ public boolean containsPublicKey(String publicKey) { Utils.checkArgument( publicKey.length() == 40, "z85 publickeys should have a length of 40 bytes but got " + publicKey.length()); reloadIfNecessary(); return publicKeys.containsKey(publicKey); } public ZMetadata getMetadata(String publicKey) { reloadIfNecessary(); return publicKeys.get(publicKey); } private void loadFiles() { final Map keys = new HashMap<>(); if (!location.exists()) { location.mkdirs(); } final Map collected = new HashMap<>(); traverseDirectory(location, new IFileVisitor() { @Override public boolean visitFile(File file) { try { ZConfig zconf = ZConfig.load(file.getAbsolutePath()); String publicKey = zconf.getValue("curve/public-key"); if (publicKey == null) { System.out.printf( "Warning!! File %s has no curve/public-key-element. SKIPPING!%n", file.getAbsolutePath()); return false; } if (publicKey.length() == 32) { // we want to store the public-key as Z85-String publicKey = ZMQ.Curve.z85Encode(publicKey.getBytes(ZMQ.CHARSET)); } assert (publicKey.length() == 40); keys.put(publicKey, ZMetadata.read(zconf)); collected.put(file, finger.print(file)); } catch (IOException e) { e.printStackTrace(); } return false; } @Override public boolean visitDir(File dir) { collected.put(dir, finger.print(dir)); return false; } }); publicKeys.clear(); publicKeys.putAll(keys); fingerprints.clear(); fingerprints.putAll(collected); } int getCertificatesCount() { reloadIfNecessary(); return publicKeys.size(); } boolean reloadIfNecessary() { if (checkForChanges()) { loadFiles(); return true; } return false; } /** * Check if files in the certificate folders have been added or removed. */ boolean checkForChanges() { // initialize with last checked files final Map presents = new HashMap<>(fingerprints); boolean modified = traverseDirectory(location, new IFileVisitor() { @Override public boolean visitFile(File file) { return modified(presents.remove(file), file); } @Override public boolean visitDir(File dir) { return modified(presents.remove(dir), dir); } }); // if some files remain, that means they have been deleted since last scan return modified || !presents.isEmpty(); } private boolean modified(byte[] fingerprint, File path) { if (!path.exists()) { // run load-files if one file is not present return true; } if (fingerprint == null) { // file was not scanned before, it has been added return true; } // true if file has been modified. return !Arrays.equals(fingerprint, finger.print(path)); } } jeromq-0.6.0/src/main/java/org/zeromq/ZConfig.java000066400000000000000000000262261455771126300217770ustar00rootroot00000000000000package org.zeromq; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** *

Lets applications load, work with, and save configuration files. * This is a minimal implementation of the ZeroMQ Property Language, * which is a simple structured text format for configuration files.

* *

Here is an example ZPL stream and corresponding config structure:

* *
 * {@code
        context
            iothreads = 1
            verbose = 1      #   Ask for a trace
        main
            type = zqueue    #  ZMQ_DEVICE type
            frontend
                option
                    hwm = 1000
                    swap = 25000000     #  25MB
                bind = 'inproc://addr1'
                bind = 'ipc://addr2'
            backend
                bind = inproc://addr3
   }

   {@code
    root                    Down = child
    |                     Across = next
    v
    context-->main
    |         |
    |         v
    |       type=queue-->frontend-->backend
    |                      |          |
    |                      |          v
    |                      |        bind=inproc://addr3
    |                      v
    |                    option-->bind=inproc://addr1-->bind=ipc://addr2
    |                      |
    |                      v
    |                    hwm=1000-->swap=25000000
    v
    iothreads=1-->verbose=false
 }
 
* *

It can put and get values and save and load them to disk:

* *
 * {@code
 * ZConfig conf = new ZConfig("root", null);
 * conf.put("/curve/public-key","abcdef");
 * String val = conf.get("/curve/public-key","fallback-defaultkey");
 * conf.save("test.cert");
 * ZConfig loaded = ZConfig.load("test.cert");
 }
*/ public class ZConfig { private interface IVisitor { void handleNode(ZConfig node, int level) throws IOException; } private static final String LEFT = "^( *)([0-9a-zA-Z\\$\\-_@\\.&\\+\\/]+)"; private static final Pattern PTRN_CONTAINER = Pattern.compile(LEFT + "( *#.*)?$"); private static final Pattern PTRN_KEYVALUE = Pattern.compile(LEFT + " = ((\"|')(.*)(\\4)|(.*?))(#.*)?$"); private final String name; private final Map children = new HashMap<>(); private final List comments = new LinkedList<>(); private String value; public ZConfig(String name, ZConfig parent) { this.name = name; if (parent != null) { parent.children.put(name, this); } } public ZConfig getChild(String name) { return children.get(name); } public Map getValues() { Map values = new HashMap<>(); fillValues("", values); return values; } private void fillValues(String prefix, Map values) { for (Entry entry : children.entrySet()) { String key = entry.getKey(); ZConfig child = entry.getValue(); assert (child != null); if (child.value != null) { values.put(prefix + key, child.value); } child.fillValues(prefix + key + '/', values); } } public String getName() { return this.name; } public String getValue(String path) { return getValue(path, null); } public String getValue(String path, String defaultValue) { String[] pathElements = path.split("/"); ZConfig current = this; for (String pathElem : pathElements) { if (pathElem.isEmpty()) { continue; } current = current.children.get(pathElem); if (current == null) { return defaultValue; } } return current.value; } /** * check if a value-path exists * @param path * @return true if value-path exists */ public boolean pathExists(String path) { String[] pathElements = path.split("/"); ZConfig current = this; for (String pathElem : pathElements) { if (pathElem.isEmpty()) { continue; } current = current.children.get(pathElem); if (current == null) { return false; } } return true; } /** * add comment * @param comment */ public void addComment(String comment) { comments.add(comment); } /** * @param path * @param value set value of config item */ public ZConfig putValue(String path, String value) { String[] pathElements = path.split("/"); ZConfig current = this; for (String pathElement : pathElements) { if (pathElement.isEmpty()) { // ignore leading slashes continue; } ZConfig container = current.children.get(pathElement); if (container == null) { container = new ZConfig(pathElement, current); } current = container; } current.value = value; return current; } public void putValues(ZConfig src) { for (Entry entry : src.getValues().entrySet()) { putValue(entry .getKey(), entry.getValue()); } } private void visit(ZConfig startNode, IVisitor handler, int level) throws IOException { handler.handleNode(startNode, level); for (ZConfig node : startNode.children.values()) { visit(node, handler, level + 1); } } /** * Saves the configuration to a file. *

* This method will overwrite contents of existing file * @param filename the path of the file to save the configuration into, or "-" to dump it to standard output * @return the saved file or null if dumped to the standard output * @throws IOException if unable to save the file. */ public File save(String filename) throws IOException { if ("-".equals(filename)) { // print to console try (Writer writer = new PrintWriter(System.out)) { save(writer); } return null; } else { // write to file final File file = new File(filename); if (file.exists()) { file.delete(); } else { // create necessary directories; file.getParentFile().mkdirs(); } try (Writer writer = new FileWriter(file)) { save(writer); } return file; } } public void save(final Writer writer) throws IOException { visit(this, (node, level) -> { // First print comments if (!node.comments.isEmpty()) { for (String comment : node.comments) { writer.append("# ").append(comment).append('\n'); } writer.append("\n"); } // now the values if (level > 0) { String prefix = level > 1 ? String.format("%" + ((level - 1) * 4) + "s", " ") : ""; writer.append(prefix); if (node.value == null) { writer.append(node.name).append("\n"); } else { writer.append(String.format("%s = \"%s\"\n", node.name, node.value)); } } }, 0); } public static ZConfig load(String filename) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { List content = new ArrayList<>(); String line = reader.readLine(); while (line != null) { boolean irrelevant = line.matches("^ *#.*|^ *[0-9]+.*") // ignore comments || line.trim().isEmpty(); // ignore empty lines; if (!irrelevant) { content.add(line); } line = reader.readLine(); } return load(new ZConfig("root", null), content, 0, new AtomicInteger()); } } private static ZConfig load(ZConfig parent, List content, int currentLevel, AtomicInteger lineNumber) { while (lineNumber.get() < content.size()) { String currentLine = content.get(lineNumber.get()); Matcher container = PTRN_CONTAINER.matcher(currentLine); if (container.find()) { ZConfig child = child(parent, container, currentLevel, currentLine, lineNumber); if (child == null) { // jump back; break; } load(child, content, currentLevel + 1, lineNumber); } else { Matcher keyvalue = PTRN_KEYVALUE.matcher(currentLine); if (keyvalue.find()) { ZConfig child = child(parent, keyvalue, currentLevel, currentLine, lineNumber); if (child == null) { // jump back; break; } String value = keyvalue.group(5); if (value == null) { value = keyvalue.group(7); } if (value != null) { value = value.trim(); } child.value = value; } else { throw new ReadException("Couldn't process line", currentLine, lineNumber); } } } return parent; } private static ZConfig child(ZConfig parent, Matcher matcher, int currentLevel, String currentLine, AtomicInteger lineNumber) { int level = matcher.group(1).length() / 4; if (level > currentLevel) { throw new ReadException("Level mismatch in line", currentLine, lineNumber); } else if (level < currentLevel) { // jump back; return null; } lineNumber.incrementAndGet(); return new ZConfig(matcher.group(2), parent); } public static class ReadException extends RuntimeException { private static final long serialVersionUID = 1L; public final int currentLineNumber; public final String currentLine; public ReadException(String message, String currentLine, AtomicInteger currentLineNumber) { super(String.format("%s %s: %s", message, currentLineNumber, currentLine)); this.currentLine = currentLine; this.currentLineNumber = currentLineNumber.get(); } } } jeromq-0.6.0/src/main/java/org/zeromq/ZContext.java000066400000000000000000000325511455771126300222140ustar00rootroot00000000000000package org.zeromq; import java.io.Closeable; import java.lang.Thread.UncaughtExceptionHandler; import java.nio.channels.Selector; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import zmq.util.Draft; import zmq.util.function.BiFunction; /** * ZContext provides a high-level ZeroMQ context management class *

* It manages open sockets in the context and automatically closes these before terminating the context. * It provides a simple way to set the linger timeout on sockets, and configure contexts for number of I/O threads. * Sets-up signal (interrupt) handling for the process. * */ public class ZContext implements Closeable { /** * Reference to underlying Context object */ private final Context context; /** * List of sockets managed by this ZContext */ private final Set sockets; /** * List of selectors managed by this ZContext */ private final Set selectors; /** * List of ZContext in the shadows */ private final Set shadows; /** * Number of io threads allocated to this context, default 1 */ private final int ioThreads; /** * Indicates if context object is owned by main thread * (useful for multi-threaded applications) */ private final boolean main; /** * Linger timeout, default 0 */ private volatile int linger; /** * Send/receive HWM for pipes */ private int pipehwm; /** * ZMQ_SNDHWM for normal sockets */ private volatile int sndhwm; /** * ZMQ_RCVHWM for normal sockets */ private volatile int rcvhwm; /** * Class Constructor */ public ZContext() { this(1); } public ZContext(int ioThreads) { this(null, ioThreads); } private ZContext(ZContext parent, int ioThreads) { if (parent == null) { this.main = true; this.context = ZMQ.context(ioThreads); this.shadows = Collections.newSetFromMap(new ConcurrentHashMap<>()); } else { this.main = false; this.context = parent.context; this.shadows = parent.shadows; this.shadows.add(this); } // Android compatibility: not using ConcurrentHashMap.newKeySet() this.sockets = Collections.newSetFromMap(new ConcurrentHashMap<>()); this.selectors = Collections.newSetFromMap(new ConcurrentHashMap<>()); this.ioThreads = ioThreads; this.linger = 0; this.pipehwm = 1000; this.sndhwm = 1000; this.rcvhwm = 1000; } /** * Destructor. Call this to gracefully terminate context and close any managed 0MQ sockets */ public void destroy() { for (Socket socket : sockets) { socket.internalClose(); } sockets.clear(); for (Selector selector : selectors) { context.close(selector); } selectors.clear(); // Only terminate context if we are on the main thread if (isMain()) { for (ZContext child : shadows) { child.close(); } context.term(); } else { shadows.remove(this); } } /** * Creates a new managed socket within this ZContext instance. * Use this to get automatic management of the socket at shutdown. *

* The newly created socket will inherited it's linger value from the one * defined for this context. * @param type * socket type * @return * Newly created Socket object */ public Socket createSocket(SocketType type) { // Create and register socket Socket socket = new Socket(this, type); socket.setRcvHWM(this.rcvhwm); socket.setSndHWM(this.sndhwm); socket.setLinger(this.linger); sockets.add(socket); return socket; } /** * @deprecated use {@link #createSocket(SocketType)} * @param type * socket type (see ZMQ static class members) * @return * Newly created Socket object */ @Deprecated public Socket createSocket(int type) { return createSocket(SocketType.type(type)); } /** * Destroys a managed socket within this context and remove * from sockets list. This method should be used only for * fast or emergency close as is set linger instead of using the * socket current value. * @param s {@link org.zeromq.ZMQ.Socket} object to destroy * @deprecated Not to be used any more. {@link org.zeromq.ZMQ.Socket} handle * the close itself. It also override linger settings. */ @Deprecated public void destroySocket(Socket s) { if (s == null) { return; } s.setLinger(linger); try { s.internalClose(); } finally { sockets.remove(s); } } /** * Close managed socket within this context and remove from sockets list. * There is no need to call this method as any {@link Socket} created by * this context will call it on termination. * @param s {@link org.zeromq.ZMQ.Socket} object to destroy */ void closeSocket(Socket s) { if (s == null) { return; } try { s.internalClose(); } finally { sockets.remove(s); } } /** * Creates a selector. It needs to be closed by {@link #closeSelector(Selector)}. * * @return a newly created selector. * @deprecated this was exposed by mistake. */ @Deprecated public Selector createSelector() { return selector(); } /** * Creates a selector. Resource will be released when context will be closed. * * @return a newly created selector. */ Selector selector() { Selector selector = context.selector(); selectors.add(selector); return selector; } /** * Closes a selector. * This is a DRAFT method, and may change without notice. * * @param selector the selector to close. It needs to have been created by {@link #createSelector()}. * @deprecated {@link #createSelector()} was exposed by mistake. while waiting for the API to disappear, this method is provided to allow releasing resources. */ @Deprecated @Draft public void closeSelector(Selector selector) { if (selectors.remove(selector)) { context.close(selector); } } public Poller createPoller(int size) { return new Poller(context, size); } /** * Creates new shadow context. * Shares same underlying org.zeromq.Context instance but has own list * of managed sockets, io thread count etc. * @param ctx Original ZContext to create shadow of * @return New ZContext * @deprecated use the instance method directly */ @Deprecated public static ZContext shadow(ZContext ctx) { return ctx.shadow(); } /** * Creates new shadow context. * Shares same underlying org.zeromq.Context instance but has own list * of managed sockets, io thread count etc. * @return New ZContext */ public ZContext shadow() { if (! main) { throw new IllegalStateException("Shadow contexts don't cast shadows"); } ZContext context = new ZContext(this, ioThreads); context.linger = linger; context.sndhwm = sndhwm; context.rcvhwm = rcvhwm; context.pipehwm = pipehwm; return context; } /** * Create an attached thread, An attached thread gets a ctx and a PAIR pipe back to its * parent. It must monitor its pipe, and exit if the pipe becomes unreadable * * @param runnable attached thread * @param args forked runnable args * @return pipe or null if there was an error */ public Socket fork(ZThread.IAttachedRunnable runnable, Object... args) { return ZThread.fork(this, runnable, args); } /** * @return the ioThreads */ public int getIoThreads() { return ioThreads; } /** * A deprecated function that does nothing. * * @param ioThreads the number of ioThreads to set * @deprecated This value should not be changed after the context is initialized. */ @Deprecated public void setIoThreads(int ioThreads) { } /** * @return the default linger for sockets. */ public int getLinger() { return linger; } /** * @param linger the linger that will inherited by created socket. */ public void setLinger(int linger) { this.linger = linger; } /** * Set initial receive HWM for all new normal sockets created in context. * You can set this per-socket after the socket is created. * The default, no matter the underlying ZeroMQ version, is 1,000. * @param rcvhwm the rcvhwm */ public void setRcvHWM(int rcvhwm) { this.rcvhwm = rcvhwm; } /** * Set initial receive HWM for all new normal sockets created in context. * You can set this per-socket after the socket is created. * The default, no matter the underlying ZeroMQ version, is 1,000. * @param sndhwm the sndhwm */ public void setSndHWM(int sndhwm) { this.sndhwm = sndhwm; } /** * Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.

* It default to the value of {@link Thread#getDefaultUncaughtExceptionHandler()} * @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no explicit handler. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { context.setUncaughtExceptionHandler(handler); } /** * @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception. */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return context.getUncaughtExceptionHandler(); } /** * In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can * be logged.

* Default to {@link Throwable#printStackTrace()} * @param handler The object to use as this thread's handler for recoverable exceptions notifications. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setNotificationExceptionHandler(UncaughtExceptionHandler handler) { context.setNotificationExceptionHandler(handler); } /** * @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run() */ public UncaughtExceptionHandler getNotificationExceptionHandler() { return context.getNotificationExceptionHandler(); } /** * Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for * performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler * will not be changed, so the factory can also be used to set it. * * @param threadFactory the thread factory used by {@link zmq.poll.Poller} * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setThreadFactor(BiFunction threadFactory) { context.setThreadFactor(threadFactory); } /** * @return the current thread factory */ public BiFunction getThreadFactory() { return context.getThreadFactory(); } /** * @return the main */ public boolean isMain() { return main; } /** * @return true if no shadow context, no sockets and no selectors are alive. */ public boolean isEmpty() { return shadows.isEmpty() && sockets.isEmpty() && selectors.isEmpty(); } /** * @param main whether or not the context is being set to main * @deprecated This value should not be changed after the context is initialized. */ @Deprecated public void setMain(boolean main) { } /** * @return the context */ public Context getContext() { return context; } /** * @param ctx sets the underlying zmq.Context associated with this ZContext wrapper object * @deprecated This value should not be changed after the ZContext is initialized. */ @Deprecated public void setContext(Context ctx) { } /** * Return a copy of the list of currently open sockets. Order is not meaningful. * @return the sockets */ public List getSockets() { return new ArrayList<>(sockets); } @Override public void close() { destroy(); } public boolean isClosed() { return context.isClosed(); } } jeromq-0.6.0/src/main/java/org/zeromq/ZEvent.java000066400000000000000000000215571455771126300216550ustar00rootroot00000000000000package org.zeromq; import java.nio.channels.SelectableChannel; import java.time.Duration; import java.util.Objects; import java.util.function.Function; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMonitor.Event; import zmq.ZError; /** * A high level wrapper for an event that stores all value as Enum or java object instead of integer, and associate a * severity with them. * The events are handled using the following rules. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Events list
EventValue typeSeverity level
CONNECTED{@link java.nio.channels.SelectableChannel}debug
CONNECT_DELAYED{@link ZMQ.Error} or null if no errordebug
CONNECT_RETRIED{@link java.time.Duration}debug
LISTENING{@link java.nio.channels.SelectableChannel}debug
BIND_FAILED{@link ZMQ.Error} or null if no errorerror
ACCEPTED{@link java.nio.channels.SelectableChannel}debug
ACCEPT_FAILED{@link ZMQ.Error} or null if no errorerror
CLOSED{@link java.nio.channels.SelectableChannel}debug
CLOSE_FAILED{@link ZMQ.Error} or null if no errorerror
DISCONNECTED{@link java.nio.channels.SelectableChannel}info
MONITOR_STOPPEDnull valuedebug
HANDSHAKE_FAILED_NO_DETAIL{@link ZMQ.Error} or null if no errorerror
HANDSHAKE_SUCCEEDED{@link ZMQ.Error} or null if no errordebug
HANDSHAKE_FAILED_PROTOCOL{@link ZMonitor.ProtocolCode}error
HANDSHAKE_FAILED_AUTH{@link java.lang.Integer}warn
HANDSHAKE_PROTOCOL{@link java.lang.Integer}debug
*/ public class ZEvent { /** * An interface used to consume events in monitor */ public interface ZEventConsummer extends zmq.ZMQ.EventConsummer { void consume(ZEvent ev); default void consume(zmq.ZMQ.Event event) { consume(new ZEvent(event, SelectableChannel.class::cast)); } } private final Event event; // To keep backward compatibility, the old value field only store integer // The resolved value (Error, channel or other) is stored in resolvedValue field. private final Object value; private final String address; private ZEvent(zmq.ZMQ.Event event, Function getResolveChannel) { this.event = ZMonitor.Event.findByCode(event.event); this.address = event.addr; this.value = resolve(this.event, event.arg, getResolveChannel); } static Object resolve(Event event, Object value, Function getResolveChannel) { switch (event) { case HANDSHAKE_FAILED_PROTOCOL: return ZMonitor.ProtocolCode.findByCode((Integer) value); case CLOSE_FAILED: case ACCEPT_FAILED: case BIND_FAILED: case HANDSHAKE_FAILED_NO_DETAIL: case CONNECT_DELAYED: case HANDSHAKE_SUCCEEDED: return ZMQ.Error.findByCode((Integer) value); case HANDSHAKE_FAILED_AUTH: case HANDSHAKE_PROTOCOL: return value; case CONNECTED: case LISTENING: case ACCEPTED: case CLOSED: case DISCONNECTED: return getResolveChannel.apply(value); case CONNECT_RETRIED: return Duration.ofMillis((Integer) value); case MONITOR_STOPPED: return null; default: assert false : "Unhandled event " + event; return null; } } public Event getEvent() { return event; } /** * Return the value of the event as a high level java object. * It returns objects of type: *

    *
  • {@link org.zeromq.ZMonitor.ProtocolCode} for a handshake protocol error.
  • *
  • {@link org.zeromq.ZMQ.Error} for any other error.
  • *
  • {@link Duration} when associated with a delay.
  • *
  • null when no relevant value available.
  • *
* @param The expected type of the returned object * @return The resolved value. */ @SuppressWarnings("unchecked") public M getValue() { return (M) value; } public String getAddress() { return address; } /** * Used to check if the event is an error. *

* Generally, any event that define the errno is * considered as an error. * @return true if the event was an error */ public boolean isError() { switch (event) { case BIND_FAILED: case ACCEPT_FAILED: case CLOSE_FAILED: case HANDSHAKE_FAILED_NO_DETAIL: case HANDSHAKE_FAILED_PROTOCOL: return true; default: return false; } } /** * Used to check if the event is a warning. *

* Generally, any event that return an authentication failure is * considered as a warning. * @return true if the event was a warning */ public boolean isWarn() { return event == Event.HANDSHAKE_FAILED_AUTH; } /** * Used to check if the event is an information. *

* Generally, any event that return an authentication failure is * considered as a warning. * @return true if the event was a warning */ public boolean isInformation() { return event == Event.DISCONNECTED; } /** * Used to check if the event is an error. *

* Generally, any event that define the errno is * considered as an error. * @return true if the event was an error */ public boolean isDebug() { switch (event) { case CONNECTED: case CONNECT_DELAYED: case CONNECT_RETRIED: case LISTENING: case ACCEPTED: case CLOSED: case MONITOR_STOPPED: case HANDSHAKE_SUCCEEDED: case HANDSHAKE_PROTOCOL: return true; default: return false; } } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o == null || getClass() != o.getClass()) { return false; } else { ZEvent zEvent = (ZEvent) o; return event == zEvent.event && Objects.equals(value, zEvent.value) && address.equals(zEvent.address); } } @Override public int hashCode() { return Objects.hash(event, value, address); } @Override public String toString() { return "ZEvent{" + "event=" + event + ", value=" + value + ", address='" + address + '\'' + '}'; } /** * Receive an event from a monitor socket. * * @param socket the monitor socket * @param flags the flags to apply to the read operation. * @return the received event or null if no message was received. * @throws ZMQException In case of errors with the monitor socket */ public static ZEvent recv(Socket socket, int flags) { zmq.ZMQ.Event e = zmq.ZMQ.Event.read(socket.base(), flags); if (socket.errno() > 0 && socket.errno() != ZError.EAGAIN) { throw new ZMQException(socket.errno()); } else if (e == null) { return null; } else { return new ZEvent(e, o -> e.getChannel(socket.getCtx())); } } /** * Receive an event from a monitor socket. * Does a blocking recv. * * @param socket the monitor socket * @return the received event or null if no message was received. * @throws ZMQException In case of errors with the monitor socket */ public static ZEvent recv(ZMQ.Socket socket) { return recv(socket, 0); } } jeromq-0.6.0/src/main/java/org/zeromq/ZFrame.java000066400000000000000000000244511455771126300216220ustar00rootroot00000000000000package org.zeromq; import java.nio.charset.Charset; import java.util.Arrays; import org.zeromq.ZMQ.Socket; import org.zeromq.util.ZData; import zmq.Msg; import zmq.SocketBase; /** * ZFrame *

* The ZFrame class provides methods to send and receive single message * frames across 0MQ sockets. A 'frame' corresponds to one underlying zmq_msg_t in the libzmq code. * When you read a frame from a socket, the more() method indicates if the frame is part of an * unfinished multipart message. The send() method normally destroys the frame, but with the ZFRAME_REUSE flag, you can send * the same frame many times. Frames are binary, and this class has no special support for text data. * */ public class ZFrame { public static final int MORE = ZMQ.SNDMORE; public static final int REUSE = 128; // no effect at java public static final int DONTWAIT = ZMQ.DONTWAIT; private boolean more; private byte[] data; private int routingId; private String group; /** * Class Constructor * Creates an empty frame. * (Useful when reading frames from a 0MQ Socket) */ protected ZFrame() { } /** * Class Constructor * Copies message data into ZFrame object * @param data * Data to copy into ZFrame object */ public ZFrame(byte[] data) { if (data != null) { this.data = data; } } /** * Class Constructor * Copies String into frame data * @param data * String to copy into ZFrame object as bytes, decoded using {@link ZMQ#CHARSET} */ public ZFrame(String data) { if (data != null) { this.data = data.getBytes(ZMQ.CHARSET); } } /** * Class Constructor * Uses internal Msg class to access routingId * @param msg internal Msg class to copy into Zframe */ protected ZFrame(zmq.Msg msg) { if (msg == null) { return; } this.data = msg.data(); this.more = msg.hasMore(); this.routingId = msg.getRoutingId(); } /** * Return frame routing ID, if the frame came from a ZMQ_SERVER socket. * Else returns zero. * @return the routing ID */ public int getRoutingId() { return routingId; } /** * Set routing ID on frame. This is used if/when the frame is sent to a * ZMQ_SERVER socket. * @param routingId the routing ID */ public void setRoutingId(int routingId) { this.routingId = routingId; } /** * Gets the group used for RADIO/DISH sockets. * @return the group name, or null. */ public String getGroup() { return group; } /** * Sets the group used for RADIO/DISH sockets. * @param group the group name, or null to unset it. */ public void setGroup(String group) { this.group = group; } /** * Destructor. */ public void destroy() { if (hasData()) { data = null; } } /** * @return the data */ public byte[] getData() { return data; } public String getString(Charset charset) { if (!hasData()) { return ""; } return new String(data, charset); } /** * @return More flag, true if last read had MORE message parts to come */ public boolean hasMore() { return more; } /** * Returns byte size of frame, if set, else 0 * @return * Number of bytes in frame data, else 0 */ public int size() { if (hasData()) { return data.length; } else { return 0; } } /** * Convenience method to ascertain if this frame contains some message data * @return * True if frame contains data */ public boolean hasData() { return data != null; } /** * Internal method to call org.zeromq.Socket send() method. * @param socket * 0MQ socket to send on * @param flags * Valid send() method flags, defined in org.zeromq.ZMQ class * @return * True if success, else False */ public boolean send(Socket socket, int flags) { Utils.checkArgument(socket != null, "socket parameter must be set"); final SocketBase base = socket.base(); final zmq.Msg msg = new Msg(data); if (group != null) { msg.setGroup(group); } int sendFlags = (flags & ZFrame.MORE) == ZFrame.MORE ? zmq.ZMQ.ZMQ_SNDMORE : 0; sendFlags |= (flags & ZFrame.DONTWAIT) == ZFrame.DONTWAIT ? zmq.ZMQ.ZMQ_DONTWAIT : 0; // Only set the routerId if the socket is a ZMQ_Server if (base instanceof zmq.socket.clientserver.Server) { msg.setRoutingId(this.routingId); } return base.send(msg, sendFlags); } /** * Sends frame to socket if it contains any data. * Frame contents are kept after the send. * @param socket * 0MQ socket to send frame * @param flags * Valid send() method flags, defined in org.zeromq.ZMQ class * @return * True if success, else False */ public boolean sendAndKeep(Socket socket, int flags) { return send(socket, flags); } /** * Sends frame to socket if it contains any data. * Frame contents are kept after the send. * Uses default behaviour of Socket.send() method, with no flags set * @param socket * 0MQ socket to send frame * @return * True if success, else False */ public boolean sendAndKeep(Socket socket) { return sendAndKeep(socket, 0); } /** * Sends frame to socket if it contains data. * Use this method to send a frame and destroy the data after. * @param socket * 0MQ socket to send frame * @param flags * Valid send() method flags, defined in org.zeromq.ZMQ class * @return * True if success, else False */ public boolean sendAndDestroy(Socket socket, int flags) { boolean ret = send(socket, flags); if (ret) { destroy(); } return ret; } /** * Sends frame to socket if it contains data. * Use this method to send an isolated frame and destroy the data after. * Uses default behaviour of Socket.send() method, with no flags set * @param socket * 0MQ socket to send frame * @return * True if success, else False */ public boolean sendAndDestroy(Socket socket) { return sendAndDestroy(socket, 0); } /** * Creates a new frame that duplicates an existing frame * @return * Duplicate of frame; message contents copied into new byte array */ public ZFrame duplicate() { return new ZFrame(this.data); } /** * Returns true if both frames have byte - for byte identical data * @param other * The other ZFrame to compare * @return * True if both ZFrames have same byte-identical data, else false */ public boolean hasSameData(ZFrame other) { if (other == null) { return false; } if (size() == other.size()) { return Arrays.equals(data, other.data); } return false; } /** * Sets new contents for frame * @param data * New byte array contents for frame */ public void reset(String data) { this.data = data.getBytes(ZMQ.CHARSET); } /** * Sets new contents for frame * @param data * New byte array contents for frame */ public void reset(byte[] data) { this.data = data; } /** * @return frame data as a printable hex string */ public String strhex() { return ZData.strhex(data); } /** * String equals. * Uses String compareTo for the comparison (lexigraphical) * @param str * String to compare with frame data * @return * True if frame body data matches given string */ public boolean streq(String str) { return ZData.streq(data, str); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ZFrame zFrame = (ZFrame) o; return Arrays.equals(data, zFrame.data); } @Override public int hashCode() { return Arrays.hashCode(data); } /** * Returns a human - readable representation of frame's data * @return * A text string or hex-encoded string if data contains any non-printable ASCII characters */ @Override public String toString() { return ZData.toString(data); } /** * Receives single frame from socket, returns the received frame object, or null if the recv * was interrupted. Does a blocking recv, if you want to not block then use * recvFrame(socket, ZMQ.DONTWAIT); * * @param socket * Socket to read from * @return * received frame, else null */ public static ZFrame recvFrame(Socket socket) { return ZFrame.recvFrame(socket, 0); } /** * Receive a new frame off the socket, Returns newly-allocated frame, or * null if there was no input waiting, or if the read was interrupted. * @param socket * Socket to read from * @param flags * Pass flags to 0MQ socket.recv call * @return * received frame, else null */ public static ZFrame recvFrame(Socket socket, int flags) { final SocketBase base = socket.base(); zmq.Msg msg = base.recv(flags); if (msg == null) { // Check to see if there was an error in recv socket.mayRaise(); return null; } ZFrame frame = new ZFrame(msg); frame.setGroup(msg.getGroup()); return frame; } public void print(String prefix) { ZData.print(System.out, prefix, getData(), size()); } } jeromq-0.6.0/src/main/java/org/zeromq/ZLoop.java000066400000000000000000000306771455771126300215100ustar00rootroot00000000000000package org.zeromq; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Poller; import zmq.util.Objects; /** * The ZLoop class provides an event-driven reactor pattern. The reactor * handles zmq.PollItem items (pollers or writers, sockets or fds), and * once-off or repeated timers. Its resolution is 1 msec. It uses a tickless * timer to reduce CPU interrupts in inactive processes. */ public class ZLoop { public interface IZLoopHandler { int handle(ZLoop loop, PollItem item, Object arg); } private static class SPoller { final PollItem item; final IZLoopHandler handler; final Object arg; int errors; // If too many errors, kill poller protected SPoller(PollItem item, IZLoopHandler handler, Object arg) { this.item = item; this.handler = handler; this.arg = arg; errors = 0; } } private static class STimer { final int delay; int times; final IZLoopHandler handler; final Object arg; long when; // Clock time when alarm goes off public STimer(int delay, int times, IZLoopHandler handler, Object arg) { this.delay = delay; this.times = times; this.handler = handler; this.arg = arg; this.when = -1; } } private final Context context; // Context managing the pollers. private final List pollers; // List of poll items private final List timers; // List of timers private int pollSize; // Size of poll set private Poller pollset; // zmq_poll set private SPoller[] pollact; // Pollers for this poll set private boolean dirty; // True if pollset needs rebuilding private boolean verbose; // True if verbose tracing wanted private final List zombies; // List of timers to kill private final List newTimers; // List of timers to add public ZLoop(Context context) { Objects.requireNonNull(context, "Context has to be supplied for ZLoop"); this.context = context; pollers = new ArrayList<>(); timers = new ArrayList<>(); zombies = new ArrayList<>(); newTimers = new ArrayList<>(); } public ZLoop(ZContext ctx) { this(ctx.getContext()); } /** * @deprecated no-op behaviour */ @Deprecated public void destroy() { // do nothing } // We hold an array of pollers that matches the pollset, so we can // register/cancel pollers orthogonally to executing the pollset // activity on pollers. Returns 0 on success, -1 on failure. private void rebuild() { pollact = null; pollSize = pollers.size(); if (pollset != null) { pollset.close(); } pollset = context.poller(pollSize); assert (pollset != null); pollact = new SPoller[pollSize]; int itemNbr = 0; for (SPoller poller : pollers) { pollset.register(poller.item); pollact[itemNbr] = poller; itemNbr++; } dirty = false; } private long ticklessTimer() { // Calculate tickless timer, up to 1 hour long tickless = System.currentTimeMillis() + 1000 * 3600; for (STimer timer : timers) { if (timer.when == -1) { timer.when = timer.delay + System.currentTimeMillis(); } if (tickless > timer.when) { tickless = timer.when; } } long timeout = tickless - System.currentTimeMillis(); if (timeout < 0) { timeout = 0; } if (verbose) { System.out.printf("I: zloop: polling for %d msec\n", timeout); } return timeout; } // -------------------------------------------------------------------------- // Register pollitem with the reactor. When the pollitem is ready, will call // the handler, passing the arg. Returns 0 if OK, -1 if there was an error. // If you register the pollitem more than once, each instance will invoke its // corresponding handler. public int addPoller(PollItem pollItem, IZLoopHandler handler, Object arg) { if (pollItem.getRawSocket() == null && pollItem.getSocket() == null) { return -1; } SPoller poller = new SPoller(pollItem, handler, arg); pollers.add(poller); dirty = true; if (verbose) { System.out.printf( "I: zloop: register %s poller (%s, %s)\n", pollItem.getSocket() != null ? pollItem.getSocket().getType() : "RAW", pollItem.getSocket(), pollItem.getRawSocket()); } return 0; } // -------------------------------------------------------------------------- // Cancel a pollitem from the reactor, specified by socket or FD. If both // are specified, uses only socket. If multiple poll items exist for same // socket/FD, cancels ALL of them. public void removePoller(PollItem pollItem) { Iterator it = pollers.iterator(); while (it.hasNext()) { SPoller p = it.next(); if (pollItem.equals(p.item)) { it.remove(); dirty = true; } } if (verbose) { System.out.printf( "I: zloop: cancel %s poller (%s, %s)", pollItem.getSocket() != null ? pollItem.getSocket().getType() : "RAW", pollItem.getSocket(), pollItem.getRawSocket()); } } // -------------------------------------------------------------------------- // Register a timer that expires after some delay and repeats some number of // times. At each expiry, will call the handler, passing the arg. To // run a timer forever, use 0 times. Returns 0 if OK, -1 if there was an // error. public int addTimer(int delay, int times, IZLoopHandler handler, Object arg) { STimer timer = new STimer(delay, times, handler, arg); // We cannot touch self->timers because we may be executing that // from inside the poll loop. So, we hold the new timer on the newTimers // list, and process that list when we're done executing timers. newTimers.add(timer); if (verbose) { System.out.printf("I: zloop: register timer delay=%d times=%d\n", delay, times); } return 0; } // -------------------------------------------------------------------------- // Cancel all timers for a specific argument (as provided in zloop_timer) // Returns 0 on success. public int removeTimer(Object arg) { Objects.requireNonNull(arg, "Argument has to be supplied"); // We cannot touch self->timers because we may be executing that // from inside the poll loop. So, we hold the arg on the zombie // list, and process that list when we're done executing timers. zombies.add(arg); if (verbose) { System.out.print("I: zloop: cancel timer\n"); } return 0; } // -------------------------------------------------------------------------- // Set verbose tracing of reactor on/off public void verbose(boolean verbose) { this.verbose = verbose; } // -------------------------------------------------------------------------- // Start the reactor. Takes control of the thread and returns when the 0MQ // context is terminated or the process is interrupted, or any event handler // returns -1. Event handlers may register new sockets and timers, and // cancel sockets. Returns 0 if interrupted, -1 if cancelled by a // handler, positive on internal error public int start() { int rc = 0; timers.addAll(newTimers); newTimers.clear(); // Recalculate all timers now for (STimer timer : timers) { timer.when = timer.delay + System.currentTimeMillis(); } // Main reactor loop while (!Thread.currentThread().isInterrupted()) { if (dirty) { // If s_rebuild_pollset() fails, break out of the loop and // return its error rebuild(); } long wait = ticklessTimer(); rc = pollset.poll(wait); if (rc == -1) { if (verbose) { System.out.printf("I: zloop: interrupted (%d)\n", rc); } rc = 0; break; // Context has been shut down } // Handle any timers that have now expired Iterator it = timers.iterator(); while (it.hasNext()) { STimer timer = it.next(); if (System.currentTimeMillis() >= timer.when && timer.when != -1) { if (verbose) { System.out.println("I: zloop: call timer handler"); } rc = timer.handler.handle(this, null, timer.arg); if (rc == -1) { break; // Timer handler signaled break } if (timer.times != 0 && --timer.times == 0) { it.remove(); } else { timer.when = timer.delay + System.currentTimeMillis(); } } } if (rc == -1) { break; // Some timer signalled break from the reactor loop } // Handle any pollers that are ready for (int itemNbr = 0; itemNbr < pollSize; itemNbr++) { SPoller poller = pollact[itemNbr]; if (pollset.getItem(itemNbr).isError()) { if (verbose) { System.out.printf( "I: zloop: can't poll %s socket (%s, %s)\n", poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW", poller.item.getSocket(), poller.item.getRawSocket()); } // Give handler one chance to handle error, then kill // poller because it'll disrupt the reactor otherwise. if (poller.errors++ > 0) { removePoller(poller.item); } } else { poller.errors = 0; // A non-error happened } if (pollset.getItem(itemNbr).readyOps() > 0) { if (verbose) { System.out.printf( "I: zloop: call %s socket handler (%s, %s)\n", poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW", poller.item.getSocket(), poller.item.getRawSocket()); } rc = poller.handler.handle(this, poller.item, poller.arg); if (rc == -1) { break; // Poller handler signaled break } } } // Now handle any timer zombies // This is going to be slow if we have many zombies for (Object arg : zombies) { it = timers.iterator(); while (it.hasNext()) { STimer timer = it.next(); if (timer.arg == arg) { it.remove(); } } } // Now handle any new timers added inside the loop timers.addAll(newTimers); newTimers.clear(); if (rc == -1) { break; } } return rc; } } jeromq-0.6.0/src/main/java/org/zeromq/ZMQ.java000066400000000000000000005735421455771126300211170ustar00rootroot00000000000000package org.zeromq; import java.io.Closeable; import java.lang.Thread.UncaughtExceptionHandler; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.Selector; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.zeromq.proto.ZPicture; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZError.CtxTerminatedException; import zmq.io.coder.IDecoder; import zmq.io.coder.IEncoder; import zmq.io.mechanism.Mechanisms; import zmq.io.net.SelectorProviderChooser; import zmq.msg.MsgAllocator; import zmq.util.Draft; import zmq.util.Z85; import zmq.util.function.BiFunction; import zmq.util.function.Consumer; /** *

The ØMQ lightweight messaging kernel is a library which extends the standard socket interfaces * with features traditionally provided by specialised messaging middleware products. * ØMQ sockets provide an abstraction of asynchronous message queues, multiple messaging patterns, * message filtering (subscriptions), seamless access to multiple transport protocols and more.

* *

Following is an overview of ØMQ concepts, describes how ØMQ abstracts standard sockets * and provides a reference manual for the functions provided by the ØMQ library.

* *

Contexts

*

Before using any ØMQ library functions you must create a {@link ZMQ.Context ØMQ context} using {@link ZMQ#context(int)}. * When you exit your application you must destroy the context using {@link ZMQ.Context#close()}.

* *

Thread safety

* A ØMQ context is thread safe and may be shared among as many application threads as necessary, * without any additional locking required on the part of the caller. *
* Individual ØMQ sockets are not thread safe except in the case * where full memory barriers are issued when migrating a socket from one thread to another. *
* In practice this means applications can create a socket in one thread with * {@link ZMQ.Context#socket(SocketType)} * and then pass it to a newly created thread as part of thread initialization. *

*

Multiple contexts

* Multiple contexts may coexist within a single application. *
* Thus, an application can use ØMQ directly and at the same time make use of any number of additional libraries * or components which themselves make use of ØMQ as long as the above guidelines regarding thread safety are adhered to. *

*

Messages

* A ØMQ message is a discrete unit of data passed between applications or components of the same application. * ØMQ messages have no internal structure and from the point of view of ØMQ itself * they are considered to be opaque binary data. *

*

Sockets

* {@link ZMQ.Socket ØMQ sockets} present an abstraction of a asynchronous message queue, * with the exact queueing semantics depending on the socket type in use. *

*

Transports

*

A ØMQ socket can use multiple different underlying transport mechanisms. * Each transport mechanism is suited to a particular purpose and has its own advantages and drawbacks.

* *

The following transport mechanisms are provided:

*
    *
  • Unicast transport using TCP
  • *
  • Local inter-process communication transport
  • *
  • Local in-process (inter-thread) communication transport
  • *
* *

Proxies

*

ØMQ provides proxies to create fanout and fan-in topologies. * A proxy connects a frontend socket to a backend socket * and switches all messages between the two sockets, opaquely. * A proxy may optionally capture all traffic to a third socket.

* *

Security

*

A ØMQ socket can select a security mechanism. Both peers must use the same security mechanism.

* *

The following security mechanisms are provided for IPC and TCP connections:

*
    *
  • Null security
  • *
  • Plain-text authentication using username and password
  • *
  • Elliptic curve authentication and encryption
  • *
*/ public class ZMQ { /** * Socket flag to indicate that more message parts are coming. */ public static final int SNDMORE = zmq.ZMQ.ZMQ_SNDMORE; // Values for flags in Socket's send and recv functions. /** * Socket flag to indicate a nonblocking send or recv mode. */ public static final int DONTWAIT = zmq.ZMQ.ZMQ_DONTWAIT; public static final int NOBLOCK = zmq.ZMQ.ZMQ_DONTWAIT; // Socket types, used when creating a Socket. Note that all of the int types here is // deprecated, use SocketType instead @Deprecated public static final int PAIR = zmq.ZMQ.ZMQ_PAIR; @Deprecated public static final int PUB = zmq.ZMQ.ZMQ_PUB; @Deprecated public static final int SUB = zmq.ZMQ.ZMQ_SUB; @Deprecated public static final int REQ = zmq.ZMQ.ZMQ_REQ; @Deprecated public static final int REP = zmq.ZMQ.ZMQ_REP; @Deprecated public static final int DEALER = zmq.ZMQ.ZMQ_DEALER; /** * Old alias for DEALER flag. * Flag to specify a XREQ socket, receiving side must be a XREP. * * @deprecated As of release 3.0 of zeromq, replaced by {@link #DEALER} */ @Deprecated public static final int XREQ = DEALER; @Deprecated public static final int ROUTER = zmq.ZMQ.ZMQ_ROUTER; /** * Old alias for ROUTER flag. * Flag to specify the receiving part of a XREQ socket. * * @deprecated As of release 3.0 of zeromq, replaced by {@link #ROUTER} */ @Deprecated public static final int XREP = ROUTER; @Deprecated public static final int PULL = zmq.ZMQ.ZMQ_PULL; @Deprecated public static final int PUSH = zmq.ZMQ.ZMQ_PUSH; @Deprecated public static final int XPUB = zmq.ZMQ.ZMQ_XPUB; @Deprecated public static final int XSUB = zmq.ZMQ.ZMQ_XSUB; @Deprecated public static final int STREAM = zmq.ZMQ.ZMQ_STREAM; /** * Flag to specify a STREAMER device. */ @Deprecated public static final int STREAMER = zmq.ZMQ.ZMQ_STREAMER; /** * Flag to specify a FORWARDER device. */ @Deprecated public static final int FORWARDER = zmq.ZMQ.ZMQ_FORWARDER; /** * Flag to specify a QUEUE device. */ @Deprecated public static final int QUEUE = zmq.ZMQ.ZMQ_QUEUE; /** * @see org.zeromq.ZMQ#PULL */ @Deprecated public static final int UPSTREAM = PULL; /** * @see org.zeromq.ZMQ#PUSH */ @Deprecated public static final int DOWNSTREAM = PUSH; /** * EVENT_CONNECTED: connection established. * The EVENT_CONNECTED event triggers when a connection has been * established to a remote peer. This can happen either synchronous * or asynchronous. Value is the FD of the newly connected socket. */ public static final int EVENT_CONNECTED = zmq.ZMQ.ZMQ_EVENT_CONNECTED; /** * EVENT_CONNECT_DELAYED: synchronous connect failed, it's being polled. * The EVENT_CONNECT_DELAYED event triggers when an immediate connection * attempt is delayed and its completion is being polled for. Value has * no meaning. */ public static final int EVENT_CONNECT_DELAYED = zmq.ZMQ.ZMQ_EVENT_CONNECT_DELAYED; /** * @see org.zeromq.ZMQ#EVENT_CONNECT_DELAYED */ @Deprecated public static final int EVENT_DELAYED = EVENT_CONNECT_DELAYED; /** * EVENT_CONNECT_RETRIED: asynchronous connect / reconnection attempt. * The EVENT_CONNECT_RETRIED event triggers when a connection attempt is * being handled by reconnect timer. The reconnect interval's recomputed * for each attempt. Value is the reconnect interval. */ public static final int EVENT_CONNECT_RETRIED = zmq.ZMQ.ZMQ_EVENT_CONNECT_RETRIED; /** * @see org.zeromq.ZMQ#EVENT_CONNECT_RETRIED */ @Deprecated public static final int EVENT_RETRIED = EVENT_CONNECT_RETRIED; /** * EVENT_LISTENING: socket bound to an address, ready to accept connections. * The EVENT_LISTENING event triggers when a socket's successfully bound to * a an interface. Value is the FD of the newly bound socket. */ public static final int EVENT_LISTENING = zmq.ZMQ.ZMQ_EVENT_LISTENING; /** * EVENT_BIND_FAILED: socket could not bind to an address. * The EVENT_BIND_FAILED event triggers when a socket could not bind to a * given interface. Value is the errno generated by the bind call. */ public static final int EVENT_BIND_FAILED = zmq.ZMQ.ZMQ_EVENT_BIND_FAILED; /** * EVENT_ACCEPTED: connection accepted to bound interface. * The EVENT_ACCEPTED event triggers when a connection from a remote peer * has been established with a socket's listen address. Value is the FD of * the accepted socket. */ public static final int EVENT_ACCEPTED = zmq.ZMQ.ZMQ_EVENT_ACCEPTED; /** * EVENT_ACCEPT_FAILED: could not accept client connection. * The EVENT_ACCEPT_FAILED event triggers when a connection attempt to a * socket's bound address fails. Value is the errno generated by accept. */ public static final int EVENT_ACCEPT_FAILED = zmq.ZMQ.ZMQ_EVENT_ACCEPT_FAILED; /** * EVENT_CLOSED: connection closed. * The EVENT_CLOSED event triggers when a connection's underlying * descriptor has been closed. Value is the former FD of the for the * closed socket. FD has been closed already! */ public static final int EVENT_CLOSED = zmq.ZMQ.ZMQ_EVENT_CLOSED; /** * EVENT_CLOSE_FAILED: connection couldn't be closed. * The EVENT_CLOSE_FAILED event triggers when a descriptor could not be * released back to the OS. Implementation note: ONLY FOR IPC SOCKETS. * Value is the errno generated by unlink. */ public static final int EVENT_CLOSE_FAILED = zmq.ZMQ.ZMQ_EVENT_CLOSE_FAILED; /** * EVENT_DISCONNECTED: broken session. * The EVENT_DISCONNECTED event triggers when the stream engine (tcp and * ipc specific) detects a corrupted / broken session. Value is the FD of * the socket. */ public static final int EVENT_DISCONNECTED = zmq.ZMQ.ZMQ_EVENT_DISCONNECTED; /** * EVENT_MONITOR_STOPPED: monitor has been stopped. * The EVENT_MONITOR_STOPPED event triggers when the monitor for a socket is * stopped. */ public static final int EVENT_MONITOR_STOPPED = zmq.ZMQ.ZMQ_EVENT_MONITOR_STOPPED; /** * EVENT_HANDSHAKE_PROTOCOL: protocol has been successfully negotiated. * The EVENT_HANDSHAKE_PROTOCOL event triggers when the stream engine (tcp and ipc) * successfully negotiated a protocol version with the peer. Value is the version number * (0 for unversioned, 3 for V3). */ public static final int EVENT_HANDSHAKE_PROTOCOL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL; /** * EVENT_ALL: all events known. * The EVENT_ALL constant can be used to set up a monitor for all known events. */ public static final int EVENT_ALL = zmq.ZMQ.ZMQ_EVENT_ALL; /** * Unspecified system errors during handshake. Event value is an errno. */ public static final int HANDSHAKE_FAILED_NO_DETAIL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL; /** * Handshake complete successfully with successful authentication (if * enabled). Event value is unused. */ public static final int HANDSHAKE_SUCCEEDED = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED; /** * Protocol errors between ZMTP peers or between server and ZAP handler. * Event value is one of ZMQ_PROTOCOL_ERROR_* */ public static final int HANDSHAKE_FAILED_PROTOCOL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL; /** * Failed authentication requests. Event value is the numeric ZAP status * code, i.e. 300, 400 or 500. */ public static final int HANDSHAKE_FAILED_AUTH = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_AUTH; public static final byte[] MESSAGE_SEPARATOR = zmq.ZMQ.MESSAGE_SEPARATOR; public static final byte[] SUBSCRIPTION_ALL = zmq.ZMQ.SUBSCRIPTION_ALL; public static final byte[] PROXY_PAUSE = zmq.ZMQ.PROXY_PAUSE; public static final byte[] PROXY_RESUME = zmq.ZMQ.PROXY_RESUME; public static final byte[] PROXY_TERMINATE = zmq.ZMQ.PROXY_TERMINATE; public static final Charset CHARSET = zmq.ZMQ.CHARSET; private ZMQ() { } /** * Create a new Context. * * @param ioThreads Number of threads to use, usually 1 is sufficient for most use cases. * @return the Context */ public static Context context(int ioThreads) { return new Context(ioThreads); } @Deprecated public static boolean device(int type, Socket frontend, Socket backend) { return zmq.ZMQ.proxy(frontend.base, backend.base, null); } /** * Starts the built-in 0MQ proxy in the current application thread. * The proxy connects a frontend socket to a backend socket. Conceptually, data flows from frontend to backend. * Depending on the socket types, replies may flow in the opposite direction. The direction is conceptual only; * the proxy is fully symmetric and there is no technical difference between frontend and backend. *

* Before calling ZMQ.proxy() you must set any socket options, and connect or bind both frontend and backend sockets. * The two conventional proxy models are: *

* ZMQ.proxy() runs in the current thread and returns only if/when the current context is closed. * * @param frontend ZMQ.Socket * @param backend ZMQ.Socket * @param capture If the capture socket is not NULL, the proxy shall send all messages, received on both * frontend and backend, to the capture socket. The capture socket should be a * ZMQ_PUB, ZMQ_DEALER, ZMQ_PUSH, or ZMQ_PAIR socket. */ public static boolean proxy(Socket frontend, Socket backend, Socket capture) { return zmq.ZMQ.proxy(frontend.base, backend.base, capture != null ? capture.base : null); } public static boolean proxy(Socket frontend, Socket backend, Socket capture, Socket control) { return zmq.ZMQ.proxy( frontend.base, backend.base, capture == null ? null : capture.base, control == null ? null : control.base); } public static int poll(Selector selector, PollItem[] items, long timeout) { return poll(selector, items, items.length, timeout); } public static int poll(Selector selector, PollItem[] items, int count, long timeout) { zmq.poll.PollItem[] pollItems = new zmq.poll.PollItem[count]; for (int i = 0; i < count; i++) { pollItems[i] = items[i].base; } return zmq.ZMQ.poll(selector, pollItems, count, timeout); } /** * @return Major version number of the ZMQ library. */ public static int getMajorVersion() { return zmq.ZMQ.ZMQ_VERSION_MAJOR; } /** * @return Major version number of the ZMQ library. */ public static int getMinorVersion() { return zmq.ZMQ.ZMQ_VERSION_MINOR; } /** * @return Major version number of the ZMQ library. */ public static int getPatchVersion() { return zmq.ZMQ.ZMQ_VERSION_PATCH; } /** * @return Full version number of the ZMQ library used for comparing versions. */ public static int getFullVersion() { return zmq.ZMQ.makeVersion(zmq.ZMQ.ZMQ_VERSION_MAJOR, zmq.ZMQ.ZMQ_VERSION_MINOR, zmq.ZMQ.ZMQ_VERSION_PATCH); } /** * @param major Version major component. * @param minor Version minor component. * @param patch Version patch component. * @return Comparible single int version number. */ public static int makeVersion(final int major, final int minor, final int patch) { return zmq.ZMQ.makeVersion(major, minor, patch); } /** * @return String version number in the form major.minor.patch. */ public static String getVersionString() { return "" + zmq.ZMQ.ZMQ_VERSION_MAJOR + "." + zmq.ZMQ.ZMQ_VERSION_MINOR + "." + zmq.ZMQ.ZMQ_VERSION_PATCH; } public static void msleep(long millis) { zmq.ZMQ.msleep(millis); } public static void sleep(long seconds) { zmq.ZMQ.sleep(seconds); } public static void sleep(long amount, TimeUnit unit) { zmq.ZMQ.sleep(amount, unit); } /** * Resolve code from errornumber. *

* Messages are taken from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html */ public enum Error { NOERROR(0, "No error"), ENOTSUP(ZError.ENOTSUP, "Not supported"), EPROTONOSUPPORT(ZError.EPROTONOSUPPORT, "Protocol not supported"), ENOBUFS(ZError.ENOBUFS, "No buffer space available"), ENETDOWN(ZError.ENETDOWN, "Network is down"), EADDRINUSE(ZError.EADDRINUSE, "Address already in use"), EADDRNOTAVAIL(ZError.EADDRNOTAVAIL, "Address not available"), ECONNREFUSED(ZError.ECONNREFUSED, "Connection refused"), EINPROGRESS(ZError.EINPROGRESS, "Operation in progress"), EHOSTUNREACH(ZError.EHOSTUNREACH, "Host unreachable"), EMTHREAD(ZError.EMTHREAD, "No thread available"), EFSM(ZError.EFSM, "Operation cannot be accomplished in current state"), ENOCOMPATPROTO(ZError.ENOCOMPATPROTO, "The protocol is not compatible with the socket type"), ETERM(ZError.ETERM, "Context was terminated"), ENOTSOCK(ZError.ENOTSOCK, "Not a socket"), EAGAIN(ZError.EAGAIN, "Resource unavailable, try again"), ENOENT(ZError.ENOENT, "No such file or directory"), EINTR(ZError.EINTR, "Interrupted function"), EACCESS(ZError.EACCESS, "Permission denied"), EFAULT(ZError.EFAULT, "Bad address"), EINVAL(ZError.EINVAL, "Invalid argument"), EISCONN(ZError.EISCONN, "Socket is connected"), ENOTCONN(ZError.ENOTCONN, "The socket is not connected"), EMSGSIZE(ZError.EMSGSIZE, "Message too large"), EAFNOSUPPORT(ZError.EAFNOSUPPORT, "Address family not supported"), ENETUNREACH(ZError.ENETUNREACH, "Network unreachable"), ECONNABORTED(ZError.ECONNABORTED, "Connection aborted"), ECONNRESET(ZError.ECONNRESET, "Connection reset"), ETIMEDOUT(ZError.ETIMEDOUT, "Connection timed out"), ENETRESET(ZError.ENETRESET, "Connection aborted by network"), EIOEXC(ZError.EIOEXC), ESOCKET(ZError.ESOCKET), EMFILE(ZError.EMFILE, "File descriptor value too large"), EPROTO(ZError.EPROTO, "Protocol error"); private static final Map map = new HashMap<>(Error.values().length); static { for (Error e : Error.values()) { map.put(e.code, e); } } private final int code; private final String message; Error(int code) { this.code = code; this.message = "errno " + code; } Error(int code, String message) { this.code = code; this.message = message; } public static Error findByCode(int code) { if (code <= 0) { return NOERROR; } else if (map.containsKey(code)) { return map.get(code); } else { throw new IllegalArgumentException("Unknown " + Error.class.getName() + " enum code: " + code); } } public int getCode() { return code; } public String getMessage() { return message; } } /** * Container for all sockets in a single process, * acting as the transport for inproc sockets, * which are the fastest way to connect threads in one process. */ public static class Context implements Closeable { private final AtomicBoolean closed = new AtomicBoolean(false); private final Ctx ctx; /** * Class constructor. * * @param ioThreads size of the threads pool to handle I/O operations. */ protected Context(int ioThreads) { ctx = zmq.ZMQ.init(ioThreads); } /** * Returns true if terminate() has been called on ctx. */ public boolean isTerminated() { return !ctx.isActive(); } /** * The size of the 0MQ thread pool to handle I/O operations. */ public int getIOThreads() { return ctx.get(zmq.ZMQ.ZMQ_IO_THREADS); } /** * Set the size of the 0MQ thread pool to handle I/O operations. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public boolean setIOThreads(int ioThreads) { return ctx.set(zmq.ZMQ.ZMQ_IO_THREADS, ioThreads); } /** * The maximum number of sockets allowed on the context */ public int getMaxSockets() { return ctx.get(zmq.ZMQ.ZMQ_MAX_SOCKETS); } /** * Sets the maximum number of sockets allowed on the context * @throws IllegalStateException If context was already initialized by the creation of a socket */ public boolean setMaxSockets(int maxSockets) { return ctx.set(zmq.ZMQ.ZMQ_MAX_SOCKETS, maxSockets); } /** * @deprecated use {@link #isBlocky()} instead */ @Deprecated public boolean getBlocky() { return isBlocky(); } public boolean isBlocky() { return ctx.get(zmq.ZMQ.ZMQ_BLOCKY) != 0; } public boolean setBlocky(boolean block) { return ctx.set(zmq.ZMQ.ZMQ_BLOCKY, block ? 1 : 0); } public boolean isIPv6() { return ctx.get(zmq.ZMQ.ZMQ_IPV6) != 0; } public boolean getIPv6() { return isIPv6(); } public boolean setIPv6(boolean ipv6) { return ctx.set(zmq.ZMQ.ZMQ_IPV6, ipv6 ? 1 : 0); } /** * Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.

* It default to the value of {@link Thread#getDefaultUncaughtExceptionHandler()} * @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no explicit handler. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { ctx.setUncaughtExceptionHandler(handler); } /** * @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception. */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return ctx.getUncaughtExceptionHandler(); } /** * In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can * be logged.

* Default to {@link Throwable#printStackTrace()} * @param handler The object to use as this thread's handler for recoverable exceptions notifications. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setNotificationExceptionHandler(UncaughtExceptionHandler handler) { ctx.setNotificationExceptionHandler(handler); } /** * @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run() */ public UncaughtExceptionHandler getNotificationExceptionHandler() { return ctx.getNotificationExceptionHandler(); } /** * Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for * performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler * will not be changed, so the factory can also be used to set it. * * @param threadFactory the thread factory used by {@link zmq.poll.Poller} * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setThreadFactor(BiFunction threadFactory) { ctx.setThreadFactory(threadFactory); } /** * @return the current thread factory */ public BiFunction getThreadFactory() { return ctx.getThreadFactory(); } /** * This is an explicit "destructor". It can be called to ensure the corresponding 0MQ * Context has been disposed of. */ public void term() { if (closed.compareAndSet(false, true)) { ctx.terminate(); } } public boolean isClosed() { return closed.get(); } /** * Creates a ØMQ socket within the specified context and return an opaque handle to the newly created socket. *
* The type argument specifies the socket type, which determines the semantics of communication over the socket. *
* The newly created socket is initially unbound, and not associated with any endpoints. *
* In order to establish a message flow a socket must first be connected * to at least one endpoint with {@link org.zeromq.ZMQ.Socket#connect(String)}, * or at least one endpoint must be created for accepting incoming connections with {@link org.zeromq.ZMQ.Socket#bind(String)}. * * @param type the socket type. * @return the newly created Socket. */ public Socket socket(SocketType type) { return new Socket(this, type); } @Deprecated public Socket socket(int type) { return socket(SocketType.type(type)); } /** * Create a new Selector within this context. * * @return the newly created Selector. */ public Selector selector() { return ctx.createSelector(); } /** * Closes a Selector that was created within this context. * * @param selector the Selector to close. * @return true if the selector was closed. otherwise false * (mostly because it was not created by the context). */ public boolean close(Selector selector) { return ctx.closeSelector(selector); } /** * Create a new Poller within this context, with a default size. * DO NOT FORGET TO CLOSE THE POLLER AFTER USE with {@link Poller#close()} * * @return the newly created Poller. */ public Poller poller() { return new Poller(this); } /** * Create a new Poller within this context, with a specified initial size. * DO NOT FORGET TO CLOSE THE POLLER AFTER USE with {@link Poller#close()} * * @param size the poller initial size. * @return the newly created Poller. */ public Poller poller(int size) { return new Poller(this, size); } /** * Destroys the ØMQ context context. * Context termination is performed in the following steps: *

    *
  • Any blocking operations currently in progress on sockets open within context * shall return immediately with an error code of ETERM. * With the exception of {@link ZMQ.Socket#close()}, any further operations on sockets * open within context shall fail with an error code of ETERM.
  • *
  • After interrupting all blocking calls, this method shall block until the following conditions are satisfied: *
      *
    • All sockets open within context have been closed with {@link ZMQ.Socket#close()}.
    • *
    • For each socket within context, all messages sent by the application with {@link ZMQ.Socket#send} have either * been physically transferred to a network peer, * or the socket's linger period set with the {@link ZMQ.Socket#setLinger(int)} socket option has expired.
    • *
    *
  • *
* Warning *
* As ZMQ_LINGER defaults to "infinite", by default this method will block indefinitely if there are any pending connects or sends. * We strongly recommend to *
    *
  • set ZMQ_LINGER to zero on all sockets
  • *
  • close all sockets, before calling this method
  • *
*/ @Override public void close() { term(); } } /** * Abstracts an asynchronous message queue, with the exact queuing semantics depending on the socket type in use. *
* Where conventional sockets transfer streams of bytes or discrete datagrams, ØMQ sockets transfer discrete messages. *

Key differences to conventional sockets

* Generally speaking, conventional sockets present a synchronous interface to either * connection-oriented reliable byte streams (SOCK_STREAM), * or connection-less unreliable datagrams (SOCK_DGRAM). *
* In comparison, ØMQ sockets present an abstraction of an asynchronous message queue, * with the exact queueing semantics depending on the socket type in use. * Where conventional sockets transfer streams of bytes or discrete datagrams, ØMQ sockets transfer discrete messages. *
* ØMQ sockets being asynchronous means that the timings of the physical connection setup and tear down, reconnect and effective delivery * are transparent to the user and organized by ØMQ itself. * Further, messages may be queued in the event that a peer is unavailable to receive them. *
* Conventional sockets allow only strict one-to-one (two peers), many-to-one (many clients, one server), or in some cases one-to-many (multicast) relationships. * With the exception of {@link ZMQ#PAIR}, ØMQ sockets may be connected to multiple endpoints using {@link ZMQ.Socket#connect(String)}, * while simultaneously accepting incoming connections from multiple endpoints bound to the socket using {@link ZMQ.Socket#bind(String)}, * thus allowing many-to-many relationships. *

Thread safety

* ØMQ sockets are not thread safe. Applications MUST NOT use a socket from multiple threads * except after migrating a socket from one thread to another with a "full fence" memory barrier. *
* ØMQ sockets are not Thread.interrupt safe. Applications MUST NOT interrupt threads using ØMQ sockets. *

Messaging patterns

*
    *
  • Request-reply *
    * The request-reply pattern is used for sending requests from a {@link ZMQ#REQ} client to one or more {@link ZMQ#REP} services, and receiving subsequent replies to each request sent. * The request-reply pattern is formally defined by http://rfc.zeromq.org/spec:28. * {@link ZMQ#REQ}, {@link ZMQ#REP}, {@link ZMQ#DEALER}, {@link ZMQ#ROUTER} socket types belong to this pattern. *
  • *
  • Publish-subscribe *
    * The publish-subscribe pattern is used for one-to-many distribution of data from a single publisher to multiple subscribers in a fan out fashion. * The publish-subscribe pattern is formally defined by http://rfc.zeromq.org/spec:29. * {@link ZMQ#SUB}, {@link ZMQ#PUB}, {@link ZMQ#XSUB}, {@link ZMQ#XPUB} socket types belong to this pattern. *
  • *
  • Pipeline *
    * The pipeline pattern is used for distributing data to nodes arranged in a pipeline. Data always flows down the pipeline, and each stage of the pipeline is connected to at least one node. * When a pipeline stage is connected to multiple nodes data is round-robined among all connected nodes. * The pipeline pattern is formally defined by http://rfc.zeromq.org/spec:30. * {@link ZMQ#PUSH}, {@link ZMQ#PULL} socket types belong to this pattern. *
  • *
  • Exclusive pair *
    * The exclusive pair pattern is used to connect a peer to precisely one other peer. This pattern is used for inter-thread communication across the inproc transport, * using {@link ZMQ#PAIR} socket type. * The exclusive pair pattern is formally defined by http://rfc.zeromq.org/spec:31. *
  • *
  • Native *
    * The native pattern is used for communicating with TCP peers and allows asynchronous requests and replies in either direction, * using {@link ZMQ#STREAM} socket type. *
  • *
*/ public static class Socket implements Closeable { // This port range is defined by IANA for dynamic or private ports // We use this when choosing a port for dynamic binding. private static final int DYNFROM = 0xc000; private static final int DYNTO = 0xffff; private final Consumer socketClose; private final SocketBase base; private final AtomicBoolean isClosed = new AtomicBoolean(false); /** * Class constructor. * * @param context a 0MQ context previously created. * @param type the socket type. */ protected Socket(Context context, SocketType type) { this(context.ctx, type.type, null); } /** * Class constructor. * * @param context a 0MQ context previously created. * @param type the socket type. */ protected Socket(ZContext context, SocketType type) { this(context.getContext().ctx, type.type, context::closeSocket); } /** * Class constructor. * * @param context a 0MQ context previously created. * @param type the socket type. * @deprecated use {@link Socket#Socket(Context, SocketType)} */ @Deprecated protected Socket(Context context, int type) { this(context.ctx, type, null); } /** * Wrap an already existing socket * @param base an already generated socket */ protected Socket(SocketBase base) { this.socketClose = s -> internalClose(); this.base = base; } private Socket(Ctx ctx, int type, Consumer socketClose) { this.base = ctx.createSocket(type); this.socketClose = socketClose != null ? socketClose : s -> internalClose(); } /** * DO NOT USE if you're trying to build a special proxy * * @return raw zmq.SocketBase */ public SocketBase base() { return base; } /** * This is an explicit "destructor". It can be called to ensure the corresponding 0MQ Socket * has been disposed of. If the socket was created from a org.zeromq.ZContext, it will remove * the reference to this socket from it. */ @Override public void close() { socketClose.accept(this); } void internalClose() { if (isClosed.compareAndSet(false, true)) { base.close(); } } /** * The 'ZMQ_TYPE option shall retrieve the socket type for the specified * 'socket'. The socket type is specified at socket creation time and * cannot be modified afterwards. * * @return the socket type. */ public int getType() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TYPE); } /** * The 'ZMQ_TYPE option shall retrieve the socket type for the specified * 'socket'. The socket type is specified at socket creation time and * cannot be modified afterwards. * * @return the socket type as an enum. */ public SocketType getSocketType() { return SocketType.type(getType()); } /** * * @return the low level {@link Ctx} associated with this socket. */ public Ctx getCtx() { return base.getCtx(); } /** * The 'ZMQ_LINGER' option shall retrieve the period for pending outbound * messages to linger in memory after closing the socket. Value of -1 means * infinite. Pending messages will be kept until they are fully transferred to * the peer. Value of 0 means that all the pending messages are dropped immediately * when socket is closed. Positive value means number of milliseconds to keep * trying to send the pending messages before discarding them. * * @return the linger period. * @see #setLinger(int) */ public int getLinger() { return base.getSocketOpt(zmq.ZMQ.ZMQ_LINGER); } private boolean setSocketOpt(int option, Object value) { try { boolean set = base.setSocketOpt(option, value); set &= base.errno() != ZError.EINVAL; return set; } catch (CtxTerminatedException e) { return false; } } /** * The ZMQ_LINGER option shall set the linger period for the specified socket. * The linger period determines how long pending messages which have yet to be sent to a peer * shall linger in memory after a socket is disconnected with disconnect or closed with close, * and further affects the termination of the socket's context with Ctx#term. * The following outlines the different behaviours: *

A value of -1 specifies an infinite linger period. * Pending messages shall not be discarded after a call to disconnect() or close(); * attempting to terminate the socket's context with Ctx#term() shall block until all pending messages have been sent to a peer. *

* The value of 0 specifies no linger period. Pending messages shall be discarded immediately after a call to disconnect() or close(). *

* Positive values specify an upper bound for the linger period in milliseconds. * Pending messages shall not be discarded after a call to disconnect() or close(); * attempting to terminate the socket's context with Ctx#term() shall block until either all pending messages have been sent to a peer, * or the linger period expires, after which any pending messages shall be discarded. * * @param value the linger period in milliseconds. * @return true if the option was set, otherwise false * @see #getLinger() * @deprecated the linger option has only integer range, use {@link #setLinger(int)} instead */ @Deprecated public boolean setLinger(long value) { return setLinger(Long.valueOf(value).intValue()); } /** * The ZMQ_LINGER option shall set the linger period for the specified socket. * The linger period determines how long pending messages which have yet to be sent to a peer * shall linger in memory after a socket is disconnected with disconnect or closed with close, * and further affects the termination of the socket's context with Ctx#term. * The following outlines the different behaviours: *

A value of -1 specifies an infinite linger period. * Pending messages shall not be discarded after a call to disconnect() or close(); * attempting to terminate the socket's context with Ctx#term() shall block until all pending messages have been sent to a peer. *

* The value of 0 specifies no linger period. Pending messages shall be discarded immediately after a call to disconnect() or close(). *

* Positive values specify an upper bound for the linger period in milliseconds. * Pending messages shall not be discarded after a call to disconnect() or close(); * attempting to terminate the socket's context with Ctx#term() shall block until either all pending messages have been sent to a peer, * or the linger period expires, after which any pending messages shall be discarded. * * @param value the linger period in milliseconds. * @return true if the option was set, otherwise false * @see #getLinger() */ public boolean setLinger(int value) { return base.setSocketOpt(zmq.ZMQ.ZMQ_LINGER, value); } /** * The ZMQ_RECONNECT_IVL option shall retrieve the initial reconnection interval for the specified socket. * The reconnection interval is the period ØMQ shall wait between attempts to reconnect * disconnected peers when using connection-oriented transports. * The value -1 means no reconnection. *

* CAUTION: The reconnection interval may be randomized by ØMQ to prevent reconnection storms in topologies with a large number of peers per socket. * * @return the reconnectIVL. * @see #setReconnectIVL(int) */ public int getReconnectIVL() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL); } /** * The ZMQ_RECONNECT_IVL option shall set the initial reconnection interval for the specified socket. * The reconnection interval is the period ØMQ shall wait between attempts * to reconnect disconnected peers when using connection-oriented transports. * The value -1 means no reconnection. * * @return true if the option was set, otherwise false * @see #getReconnectIVL() * @deprecated reconnect interval option uses integer range, use {@link #setReconnectIVL(int)} instead */ @Deprecated public boolean setReconnectIVL(long value) { return setReconnectIVL(Long.valueOf(value).intValue()); } /** * The ZMQ_RECONNECT_IVL option shall set the initial reconnection interval for the specified socket. * The reconnection interval is the period ØMQ shall wait between attempts * to reconnect disconnected peers when using connection-oriented transports. * The value -1 means no reconnection. * * @return true if the option was set, otherwise false. * @see #getReconnectIVL() */ public boolean setReconnectIVL(int value) { return base.setSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL, value); } /** * The ZMQ_BACKLOG option shall retrieve the maximum length of the queue * of outstanding peer connections for the specified socket; * this only applies to connection-oriented transports. * For details refer to your operating system documentation for the listen function. * * @return the the maximum length of the queue of outstanding peer connections. * @see #setBacklog(int) */ public int getBacklog() { return base.getSocketOpt(zmq.ZMQ.ZMQ_BACKLOG); } /** * The ZMQ_BACKLOG option shall set the maximum length * of the queue of outstanding peer connections for the specified socket; * this only applies to connection-oriented transports. * For details refer to your operating system documentation for the listen function. * * @param value the maximum length of the queue of outstanding peer connections. * @return true if the option was set, otherwise false. * @see #getBacklog() * @deprecated this option uses integer range, use {@link #setBacklog(int)} instead. */ @Deprecated public boolean setBacklog(long value) { return setBacklog(Long.valueOf(value).intValue()); } /** * The ZMQ_BACKLOG option shall set the maximum length * of the queue of outstanding peer connections for the specified socket; * this only applies to connection-oriented transports. * For details refer to your operating system documentation for the listen function. * * @param value the maximum length of the queue of outstanding peer connections. * @return true if the option was set, otherwise false. * @see #getBacklog() */ public boolean setBacklog(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_BACKLOG, value); } /** * The ZMQ_HANDSHAKE_IVL option shall retrieve the maximum handshake interval * for the specified socket. * Handshaking is the exchange of socket configuration information * (socket type, identity, security) that occurs when a connection is first opened, * only for connection-oriented transports. * If handshaking does not complete within the configured time, * the connection shall be closed. The value 0 means no handshake time limit. * * @return the maximum handshake interval. * @see #setHandshakeIvl(int) */ public int getHandshakeIvl() { return base.getSocketOpt(zmq.ZMQ.ZMQ_HANDSHAKE_IVL); } /** * The ZMQ_HEARTBEAT_IVL option shall set the interval * between sending ZMTP heartbeats for the specified socket. * If this option is set and is greater than 0, * then a PING ZMTP command will be sent every ZMQ_HEARTBEAT_IVL milliseconds. * * @return heartbeat interval in milliseconds */ public int getHeartbeatIvl() { return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_IVL); } /** * The ZMQ_HEARTBEAT_TIMEOUT option shall set * how long to wait before timing-out a connection * after sending a PING ZMTP command and not receiving any traffic. * This option is only valid if ZMQ_HEARTBEAT_IVL is also set, * and is greater than 0. The connection will time out * if there is no traffic received after sending the PING command, * but the received traffic does not have to be a PONG command * - any received traffic will cancel the timeout. * * @return heartbeat timeout in milliseconds */ public int getHeartbeatTimeout() { return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TIMEOUT); } /** * The ZMQ_HEARTBEAT_TTL option shall set the timeout * on the remote peer for ZMTP heartbeats. * If this option is greater than 0, * the remote side shall time out the connection * if it does not receive any more traffic within the TTL period. * This option does not have any effect if ZMQ_HEARTBEAT_IVL is not set or is 0. * Internally, this value is rounded down to the nearest decisecond, * any value less than 100 will have no effect. * * @return heartbeat time-to-live in milliseconds */ public int getHeartbeatTtl() { return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TTL); } /** * The ZMQ_HEARTBEAT_CONTEXT option shall set the ping context * of the peer for ZMTP heartbeats. *

* This API is in DRAFT state and is subject to change at ANY time until declared stable. *

* If this option is set, every ping message sent for heartbeat will contain this context. * * @return the context to be sent with ping messages. Empty array by default. */ @Draft public byte[] getHeartbeatContext() { return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_HEARTBEAT_CONTEXT); } /** * The ZMQ_HANDSHAKE_IVL option shall set the maximum handshake interval for the specified socket. * Handshaking is the exchange of socket configuration information (socket type, identity, security) * that occurs when a connection is first opened, only for connection-oriented transports. * If handshaking does not complete within the configured time, the connection shall be closed. * The value 0 means no handshake time limit. * * @param maxHandshakeIvl the maximum handshake interval * @return true if the option was set, otherwise false * @see #getHandshakeIvl() */ public boolean setHandshakeIvl(int maxHandshakeIvl) { return setSocketOpt(zmq.ZMQ.ZMQ_HANDSHAKE_IVL, maxHandshakeIvl); } /** * The ZMQ_HEARTBEAT_IVL option shall set the interval * between sending ZMTP heartbeats for the specified socket. * If this option is set and is greater than 0, * then a PING ZMTP command will be sent every ZMQ_HEARTBEAT_IVL milliseconds. * * @param heartbeatIvl heartbeat interval in milliseconds * @return true if the option was set, otherwise false */ public boolean setHeartbeatIvl(int heartbeatIvl) { return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_IVL, heartbeatIvl); } /** * The ZMQ_HEARTBEAT_TIMEOUT option shall set * how long to wait before timing-out a connection * after sending a PING ZMTP command and not receiving any traffic. * This option is only valid if ZMQ_HEARTBEAT_IVL is also set, * and is greater than 0. The connection will time out * if there is no traffic received after sending the PING command, * but the received traffic does not have to be a PONG command * - any received traffic will cancel the timeout. * * @param heartbeatTimeout heartbeat timeout in milliseconds * @return true if the option was set, otherwise false */ public boolean setHeartbeatTimeout(int heartbeatTimeout) { return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TIMEOUT, heartbeatTimeout); } /** * The ZMQ_HEARTBEAT_TTL option shall set the timeout * on the remote peer for ZMTP heartbeats. * If this option is greater than 0, * the remote side shall time out the connection * if it does not receive any more traffic within the TTL period. * This option does not have any effect if ZMQ_HEARTBEAT_IVL is not set or is 0. * Internally, this value is rounded down to the nearest decisecond, * any value less than 100 will have no effect. * * @param heartbeatTtl heartbeat time-to-live in milliseconds * @return true if the option was set, otherwise false */ public boolean setHeartbeatTtl(int heartbeatTtl) { return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TTL, heartbeatTtl); } /** * The ZMQ_HEARTBEAT_CONTEXT option shall set the ping context * of the peer for ZMTP heartbeats. *

* This API is in DRAFT state and is subject to change at ANY time until declared stable. *

* If this option is set, every ping message sent for heartbeat will contain this context. * * @param pingContext the context to be sent with ping messages. * @return true if the option was set, otherwise false */ @Draft public boolean setHeartbeatContext(byte[] pingContext) { return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_CONTEXT, pingContext); } /** * Retrieve the IP_TOS option for the socket. * * @return the value of the Type-Of-Service set for the socket. * @see #setTos(int) */ public int getTos() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TOS); } /** * Sets the ToS fields (Differentiated services (DS) * and Explicit Congestion Notification (ECN) field of the IP header. * The ToS field is typically used to specify a packets priority. * The availability of this option is dependent on intermediate network equipment * that inspect the ToS field andprovide a path for low-delay, high-throughput, highly-reliable service, etc. * * @return true if the option was set, otherwise false. * @see #getTos() */ public boolean setTos(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_TOS, value); } /** * The ZMQ_RECONNECT_IVL_MAX option shall retrieve the maximum reconnection interval for the specified socket. * This is the maximum period ØMQ shall wait between attempts to reconnect. * On each reconnect attempt, the previous interval shall be doubled untill ZMQ_RECONNECT_IVL_MAX is reached. * This allows for exponential backoff strategy. * Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL. * * @return the reconnectIVLMax. * @see #setReconnectIVLMax(int) */ public int getReconnectIVLMax() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL_MAX); } /** * The ZMQ_RECONNECT_IVL_MAX option shall set the maximum reconnection interval for the specified socket. * This is the maximum period ØMQ shall wait between attempts to reconnect. * On each reconnect attempt, the previous interval shall be doubled until ZMQ_RECONNECT_IVL_MAX is reached. * This allows for exponential backoff strategy. * Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL. * * @return true if the option was set, otherwise false * @see #getReconnectIVLMax() * @deprecated this option uses integer range, use {@link #setReconnectIVLMax(int)} instead */ @Deprecated public boolean setReconnectIVLMax(long value) { return setReconnectIVLMax(Long.valueOf(value).intValue()); } /** * The ZMQ_RECONNECT_IVL_MAX option shall set the maximum reconnection interval for the specified socket. * This is the maximum period ØMQ shall wait between attempts to reconnect. * On each reconnect attempt, the previous interval shall be doubled until ZMQ_RECONNECT_IVL_MAX is reached. * This allows for exponential backoff strategy. * Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL. * * @return true if the option was set, otherwise false * @see #getReconnectIVLMax() */ public boolean setReconnectIVLMax(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL_MAX, value); } /** * The option shall retrieve limit for the inbound messages. * If a peer sends a message larger than ZMQ_MAXMSGSIZE it is disconnected. * Value of -1 means no limit. * * @return the maxMsgSize. * @see #setMaxMsgSize(long) */ public long getMaxMsgSize() { return (Long) base.getSocketOptx(zmq.ZMQ.ZMQ_MAXMSGSIZE); } /** * Limits the size of the inbound message. * If a peer sends a message larger than ZMQ_MAXMSGSIZE it is disconnected. * Value of -1 means no limit. * * @return true if the option was set, otherwise false * @see #getMaxMsgSize() */ public boolean setMaxMsgSize(long value) { return setSocketOpt(zmq.ZMQ.ZMQ_MAXMSGSIZE, value); } /** * The ZMQ_SNDHWM option shall return the high water mark for outbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. * * @return the SndHWM. * @see #setSndHWM(int) */ public int getSndHWM() { return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDHWM); } /** * The ZMQ_SNDHWM option shall set the high water mark for outbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. *

* CAUTION: ØMQ does not guarantee that the socket will accept as many as ZMQ_SNDHWM messages, * and the actual limit may be as much as 60-70% lower depending on the flow of messages on the socket. * * @return true if the option was set, otherwise false. * @see #getSndHWM() * @deprecated this option uses integer range, use {@link #setSndHWM(int)} instead */ @Deprecated public boolean setSndHWM(long value) { return setSndHWM(Long.valueOf(value).intValue()); } /** * The ZMQ_SNDHWM option shall set the high water mark for outbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. *

* CAUTION: ØMQ does not guarantee that the socket will accept as many as ZMQ_SNDHWM messages, * and the actual limit may be as much as 60-70% lower depending on the flow of messages on the socket. * * @param value * @return true if the option was set, otherwise false. * @see #getSndHWM() */ public boolean setSndHWM(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_SNDHWM, value); } /** * The ZMQ_RCVHWM option shall return the high water mark for inbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. * * @return the recvHWM period. * @see #setRcvHWM(int) */ public int getRcvHWM() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVHWM); } /** * The ZMQ_RCVHWM option shall set the high water mark for inbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. * * @return true if the option was set, otherwise false * @see #getRcvHWM() * @deprecated this option uses integer range, use {@link #setRcvHWM(int)} instead */ @Deprecated public boolean setRcvHWM(long value) { return setRcvHWM(Long.valueOf(value).intValue()); } /** * The ZMQ_RCVHWM option shall set the high water mark for inbound messages on the specified socket. * The high water mark is a hard limit on the maximum number of outstanding messages ØMQ * shall queue in memory for any single peer that the specified socket is communicating with. * A value of zero means no limit. * If this limit has been reached the socket shall enter an exceptional state and depending on the socket type, * ØMQ shall take appropriate action such as blocking or dropping sent messages. * Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type. * * @param value * @return true if the option was set, otherwise false. * @see #getRcvHWM() */ public boolean setRcvHWM(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_RCVHWM, value); } /** * @return the High Water Mark. * @see #setHWM(int) */ @Deprecated public int getHWM() { return -1; } /** * The 'ZMQ_HWM' option shall set the high water mark for the specified 'socket'. The high * water mark is a hard limit on the maximum number of outstanding messages 0MQ shall queue * in memory for any single peer that the specified 'socket' is communicating with. *

* If this limit has been reached the socket shall enter an exceptional state and depending * on the socket type, 0MQ shall take appropriate action such as blocking or dropping sent * messages. Refer to the individual socket descriptions in the man page of zmq_socket[3] for * details on the exact action taken for each socket type. * * @param hwm the number of messages to queue. * @return true if the option was set, otherwise false. * @deprecated this option uses integer range, use {@link #setHWM(int)} instead */ @Deprecated public boolean setHWM(long hwm) { boolean set = true; set |= setSndHWM(hwm); set |= setRcvHWM(hwm); return set; } /** * The 'ZMQ_HWM' option shall set the high water mark for the specified 'socket'. The high * water mark is a hard limit on the maximum number of outstanding messages 0MQ shall queue * in memory for any single peer that the specified 'socket' is communicating with. *

* If this limit has been reached the socket shall enter an exceptional state and depending * on the socket type, 0MQ shall take appropriate action such as blocking or dropping sent * messages. Refer to the individual socket descriptions in the man page of zmq_socket[3] for * details on the exact action taken for each socket type. * * @param hwm the number of messages to queue. * @return true if the option was set, otherwise false */ public boolean setHWM(int hwm) { boolean set = false; set |= setSndHWM(hwm); set |= setRcvHWM(hwm); return set; } /** * @return the number of messages to swap at most. * @see #setSwap(long) */ @Deprecated public long getSwap() { // not support at zeromq 3 return -1L; } /** * If set, a socket shall keep only one message in its inbound/outbound queue, * this message being the last message received/the last message to be sent. * Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options. * Does not support multi-part messages, in particular, * only one part of it is kept in the socket internal queue. * * @param conflate true to keep only one message, false for standard behaviour. * @return true if the option was set, otherwise false. * @see #isConflate() */ public boolean setConflate(boolean conflate) { return setSocketOpt(zmq.ZMQ.ZMQ_CONFLATE, conflate); } /** * If in conflate mode, a socket shall keep only one message in its inbound/outbound queue, * this message being the last message received/the last message to be sent. * Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options. * Does not support multi-part messages, in particular, * only one part of it is kept in the socket internal queue. * * @return true to keep only one message, false for standard behaviour. * @see #setConflate(boolean) */ public boolean isConflate() { return base.getSocketOpt(zmq.ZMQ.ZMQ_CONFLATE) != 0; } /** * If in conflate mode, a socket shall keep only one message in its inbound/outbound queue, * this message being the last message received/the last message to be sent. * Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options. * Does not support multi-part messages, in particular, * only one part of it is kept in the socket internal queue. * * @return true to keep only one message, false for standard behaviour. * @see #setConflate(boolean) */ public boolean getConflate() { return isConflate(); } /** * Get the Swap. The 'ZMQ_SWAP' option shall set the disk offload (swap) size for the * specified 'socket'. A socket which has 'ZMQ_SWAP' set to a non-zero value may exceed its * high water mark; in this case outstanding messages shall be offloaded to storage on disk * rather than held in memory. * * @param value The value of 'ZMQ_SWAP' defines the maximum size of the swap space in bytes. */ @Deprecated public boolean setSwap(long value) { throw new UnsupportedOperationException(); } /** * @return the affinity. * @see #setAffinity(long) */ public long getAffinity() { return (Long) base.getSocketOptx(zmq.ZMQ.ZMQ_AFFINITY); } /** * Get the Affinity. The 'ZMQ_AFFINITY' option shall set the I/O thread affinity for newly * created connections on the specified 'socket'. *

* Affinity determines which threads from the 0MQ I/O thread pool associated with the * socket's _context_ shall handle newly created connections. A value of zero specifies no * affinity, meaning that work shall be distributed fairly among all 0MQ I/O threads in the * thread pool. For non-zero values, the lowest bit corresponds to thread 1, second lowest * bit to thread 2 and so on. For example, a value of 3 specifies that subsequent * connections on 'socket' shall be handled exclusively by I/O threads 1 and 2. *

* See also in the man page of init[3] for details on allocating the number of I/O threads for a * specific _context_. * * @param value the io_thread affinity. * @return true if the option was set, otherwise false */ public boolean setAffinity(long value) { return setSocketOpt(zmq.ZMQ.ZMQ_AFFINITY, value); } /** * @return the Identitiy. * @see #setIdentity(byte[]) */ public byte[] getIdentity() { return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_IDENTITY); } /** * The 'ZMQ_IDENTITY' option shall set the identity of the specified 'socket'. Socket * identity determines if existing 0MQ infastructure (_message queues_, _forwarding * devices_) shall be identified with a specific application and persist across multiple * runs of the application. *

* If the socket has no identity, each run of an application is completely separate from * other runs. However, with identity set the socket shall re-use any existing 0MQ * infrastructure configured by the previous run(s). Thus the application may receive * messages that were sent in the meantime, _message queue_ limits shall be shared with * previous run(s) and so on. *

* Identity should be at least one byte and at most 255 bytes long. Identities starting with * binary zero are reserved for use by 0MQ infrastructure. * * @param identity * @return true if the option was set, otherwise false */ public boolean setIdentity(byte[] identity) { return setSocketOpt(zmq.ZMQ.ZMQ_IDENTITY, identity); } /** * @return the Rate. * @see #setRate(long) */ public long getRate() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RATE); } /** * The 'ZMQ_RATE' option shall set the maximum send or receive data rate for multicast * transports such as in the man page of zmq_pgm[7] using the specified 'socket'. * * @param value maximum send or receive data rate for multicast, default 100 * @return true if the option was set, otherwise false */ public boolean setRate(long value) { throw new UnsupportedOperationException(); } /** * The ZMQ_RECOVERY_IVL option shall retrieve the recovery interval for multicast transports * using the specified socket. The recovery interval determines the maximum time in milliseconds * that a receiver can be absent from a multicast group before unrecoverable data loss will occur. * * @return the RecoveryIntervall. * @see #setRecoveryInterval(long) */ public long getRecoveryInterval() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RECOVERY_IVL); } /** * The 'ZMQ_RECOVERY_IVL' option shall set the recovery interval for multicast transports * using the specified 'socket'. The recovery interval determines the maximum time in * seconds that a receiver can be absent from a multicast group before unrecoverable data * loss will occur. *

* CAUTION: Exercise care when setting large recovery intervals as the data needed for * recovery will be held in memory. For example, a 1 minute recovery interval at a data rate * of 1Gbps requires a 7GB in-memory buffer. {Purpose of this Method} * * @param value recovery interval for multicast in milliseconds, default 10000 * @return true if the option was set, otherwise false. * @see #getRecoveryInterval() */ public boolean setRecoveryInterval(long value) { throw new UnsupportedOperationException(); } /** * The default behavior of REQ sockets is to rely on the ordering of messages * to match requests and responses and that is usually sufficient. * When this option is set to true, the REQ socket will prefix outgoing messages * with an extra frame containing a request id. * That means the full message is (request id, identity, 0, user frames…). * The REQ socket will discard all incoming messages that don't begin with these two frames. * See also ZMQ_REQ_RELAXED. * * @param correlate Whether to enable outgoing request ids. * @return true if the option was set, otherwise false * @see #getReqCorrelate() */ public boolean setReqCorrelate(boolean correlate) { return setSocketOpt(zmq.ZMQ.ZMQ_REQ_CORRELATE, correlate); } /** * The default behavior of REQ sockets is to rely on the ordering of messages * to match requests and responses and that is usually sufficient. * When this option is set to true, the REQ socket will prefix outgoing messages * with an extra frame containing a request id. * That means the full message is (request id, identity, 0, user frames…). * The REQ socket will discard all incoming messages that don't begin with these two frames. * * @return state of the ZMQ_REQ_CORRELATE option. * @see #setReqCorrelate(boolean) */ @Deprecated public boolean getReqCorrelate() { throw new UnsupportedOperationException(); } /** * By default, a REQ socket does not allow initiating a new request with zmq_send(3) * until the reply to the previous one has been received. * When set to true, sending another message is allowed and has the effect of disconnecting * the underlying connection to the peer from which the reply was expected, * triggering a reconnection attempt on transports that support it. * The request-reply state machine is reset and a new request is sent to the next available peer. * If set to true, also enable ZMQ_REQ_CORRELATE to ensure correct matching of requests and replies. * Otherwise a late reply to an aborted request can be reported as the reply to the superseding request. * * @param relaxed * @return true if the option was set, otherwise false * @see #getReqRelaxed() */ public boolean setReqRelaxed(boolean relaxed) { return setSocketOpt(zmq.ZMQ.ZMQ_REQ_RELAXED, relaxed); } /** * By default, a REQ socket does not allow initiating a new request with zmq_send(3) * until the reply to the previous one has been received. * When set to true, sending another message is allowed and has the effect of disconnecting * the underlying connection to the peer from which the reply was expected, * triggering a reconnection attempt on transports that support it. * The request-reply state machine is reset and a new request is sent to the next available peer. * If set to true, also enable ZMQ_REQ_CORRELATE to ensure correct matching of requests and replies. * Otherwise a late reply to an aborted request can be reported as the reply to the superseding request. * * @return state of the ZMQ_REQ_RELAXED option. * @see #setReqRelaxed(boolean) */ @Deprecated public boolean getReqRelaxed() { throw new UnsupportedOperationException(); } /** * @return the Multicast Loop. * @see #setMulticastLoop(boolean) */ @Deprecated public boolean hasMulticastLoop() { return false; } /** * The 'ZMQ_MCAST_LOOP' option shall control whether data sent via multicast transports * using the specified 'socket' can also be received by the sending host via loopback. A * value of zero disables the loopback functionality, while the default value of 1 enables * the loopback functionality. Leaving multicast loopback enabled when it is not required * can have a negative impact on performance. Where possible, disable 'ZMQ_MCAST_LOOP' in * production environments. * * @param multicastLoop */ @Deprecated public boolean setMulticastLoop(boolean multicastLoop) { throw new UnsupportedOperationException(); } /** * @return the Multicast Hops. * @see #setMulticastHops(long) */ public long getMulticastHops() { return base.getSocketOpt(zmq.ZMQ.ZMQ_MULTICAST_HOPS); } /** * Sets the time-to-live field in every multicast packet sent from this socket. * The default is 1 which means that the multicast packets don't leave the local * network. * * @param value time-to-live field in every multicast packet, default 1 */ public boolean setMulticastHops(long value) { throw new UnsupportedOperationException(); } /** * Retrieve the timeout for recv operation on the socket. * If the value is 0, recv will return immediately, * with null if there is no message to receive. * If the value is -1, it will block until a message is available. * For all other values, it will wait for a message for that amount of time * before returning with a null and an EAGAIN error. * * @return the Receive Timeout in milliseconds. * @see #setReceiveTimeOut(int) */ public int getReceiveTimeOut() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVTIMEO); } /** * Sets the timeout for receive operation on the socket. If the value is 0, recv * will return immediately, with null if there is no message to receive. * If the value is -1, it will block until a message is available. For all other * values, it will wait for a message for that amount of time before returning with * a null and an EAGAIN error. * * @param value Timeout for receive operation in milliseconds. Default -1 (infinite) * @return true if the option was set, otherwise false. * @see #getReceiveTimeOut() */ public boolean setReceiveTimeOut(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_RCVTIMEO, value); } /** * Retrieve the timeout for send operation on the socket. * If the value is 0, send will return immediately, with a false and an EAGAIN error if the message cannot be sent. * If the value is -1, it will block until the message is sent. * For all other values, it will try to send the message for that amount of time before returning with false and an EAGAIN error. * * @return the Send Timeout in milliseconds. * @see #setSendTimeOut(int) */ public int getSendTimeOut() { return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDTIMEO); } /** * Sets the timeout for send operation on the socket. If the value is 0, send * will return immediately, with a false if the message cannot be sent. * If the value is -1, it will block until the message is sent. For all other * values, it will try to send the message for that amount of time before * returning with false and an EAGAIN error. * * @param value Timeout for send operation in milliseconds. Default -1 (infinite) * @return true if the option was set, otherwise false. * @see #getSendTimeOut() */ public boolean setSendTimeOut(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_SNDTIMEO, value); } /** * Override SO_KEEPALIVE socket option (where supported by OS) to enable keep-alive packets for a socket * connection. Possible values are -1, 0, 1. The default value -1 will skip all overrides and do the OS default. * * @param value The value of 'ZMQ_TCP_KEEPALIVE' to turn TCP keepalives on (1) or off (0). * @return true if the option was set, otherwise false. */ @Deprecated public boolean setTCPKeepAlive(long value) { return setTCPKeepAlive(Long.valueOf(value).intValue()); } /** * @return the keep alive setting. * @see #setTCPKeepAlive(long) */ @Deprecated public long getTCPKeepAliveSetting() { return getTCPKeepAlive(); } /** * Override TCP_KEEPCNT socket option (where supported by OS). The default value -1 will skip all overrides and * do the OS default. * * @param value The value of 'ZMQ_TCP_KEEPALIVE_CNT' defines the number of keepalives before death. * @return true if the option was set, otherwise false. */ public boolean setTCPKeepAliveCount(long value) { return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_CNT, Long.valueOf(value).intValue()); } /** * @return the keep alive count. * @see #setTCPKeepAliveCount(long) */ public long getTCPKeepAliveCount() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_CNT); } /** * Override TCP_KEEPINTVL socket option (where supported by OS). The default value -1 will skip all overrides * and do the OS default. * * @param value The value of 'ZMQ_TCP_KEEPALIVE_INTVL' defines the interval between keepalives. Unit is OS * dependent. * @return true if the option was set, otherwise false. */ public boolean setTCPKeepAliveInterval(long value) { return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_INTVL, Long.valueOf(value).intValue()); } /** * @return the keep alive interval. * @see #setTCPKeepAliveInterval(long) */ public long getTCPKeepAliveInterval() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_INTVL); } /** * Override TCP_KEEPCNT (or TCP_KEEPALIVE on some OS) socket option (where supported by OS). The default value * -1 will skip all overrides and do the OS default. * * @param value The value of 'ZMQ_TCP_KEEPALIVE_IDLE' defines the interval between the last data packet sent * over the socket and the first keepalive probe. Unit is OS dependent. * @return true if the option was set, otherwise false */ public boolean setTCPKeepAliveIdle(long value) { return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_IDLE, Long.valueOf(value).intValue()); } /** * @return the keep alive idle value. * @see #setTCPKeepAliveIdle(long) */ public long getTCPKeepAliveIdle() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_IDLE); } /** * The ZMQ_SNDBUF option shall retrieve the underlying kernel transmit buffer size for the specified socket. * For details refer to your operating system documentation for the SO_SNDBUF socket option. * * @return the kernel send buffer size. * @see #setSendBufferSize(int) */ public int getSendBufferSize() { return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDBUF); } /** * The 'ZMQ_SNDBUF' option shall set the underlying kernel transmit buffer size for the * 'socket' to the specified size in bytes. A value of zero means leave the OS default * unchanged. For details please refer to your operating system documentation for the * 'SO_SNDBUF' socket option. * * @param value underlying kernel transmit buffer size for the 'socket' in bytes * A value of zero means leave the OS default unchanged. * @return true if the option was set, otherwise false * @see #getSendBufferSize() * @deprecated this option uses integer range, use {@link #setSendBufferSize(int)} instead */ @Deprecated public boolean setSendBufferSize(long value) { return setSendBufferSize(Long.valueOf(value).intValue()); } /** * The 'ZMQ_SNDBUF' option shall set the underlying kernel transmit buffer size for the * 'socket' to the specified size in bytes. A value of zero means leave the OS default * unchanged. For details please refer to your operating system documentation for the * 'SO_SNDBUF' socket option. * * @param value underlying kernel transmit buffer size for the 'socket' in bytes * A value of zero means leave the OS default unchanged. * @return true if the option was set, otherwise false * @see #getSendBufferSize() */ public boolean setSendBufferSize(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_SNDBUF, value); } /** * The ZMQ_RCVBUF option shall retrieve the underlying kernel receive buffer size for the specified socket. * For details refer to your operating system documentation for the SO_RCVBUF socket option. * * @return the kernel receive buffer size. * @see #setReceiveBufferSize(int) */ public int getReceiveBufferSize() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVBUF); } /** * The 'ZMQ_RCVBUF' option shall set the underlying kernel receive buffer size for the * 'socket' to the specified size in bytes. * For details refer to your operating system documentation for the 'SO_RCVBUF' * socket option. * * @param value Underlying kernel receive buffer size for the 'socket' in bytes. * A value of zero means leave the OS default unchanged. * @return true if the option was set, otherwise false * @see #getReceiveBufferSize() * @deprecated this option uses integer range, use {@link #setReceiveBufferSize(int)} instead */ @Deprecated public boolean setReceiveBufferSize(long value) { return setReceiveBufferSize(Long.valueOf(value).intValue()); } /** * The 'ZMQ_RCVBUF' option shall set the underlying kernel receive buffer size for the * 'socket' to the specified size in bytes. * For details refer to your operating system documentation for the 'SO_RCVBUF' * socket option. * * @param value Underlying kernel receive buffer size for the 'socket' in bytes. * A value of zero means leave the OS default unchanged. * @return true if the option was set, otherwise false * @see #getReceiveBufferSize() */ public boolean setReceiveBufferSize(int value) { return setSocketOpt(zmq.ZMQ.ZMQ_RCVBUF, value); } /** * The 'ZMQ_RCVMORE' option shall return a boolean value indicating if the multi-part * message currently being read from the specified 'socket' has more message parts to * follow. If there are no message parts to follow or if the message currently being read is * not a multi-part message a value of zero shall be returned. Otherwise, a value of 1 shall * be returned. * * @return true if there are more messages to receive. */ public boolean hasReceiveMore() { return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVMORE) == 1; } /** * The 'ZMQ_FD' option shall retrieve file descriptor associated with the 0MQ * socket. The descriptor can be used to integrate 0MQ socket into an existing * event loop. It should never be used for anything else than polling -- such as * reading or writing. The descriptor signals edge-triggered IN event when * something has happened within the 0MQ socket. It does not necessarily mean that * the messages can be read or written. Check ZMQ_EVENTS option to find out whether * the 0MQ socket is readable or writeable. * * @return the underlying file descriptor. */ public SelectableChannel getFD() { return (SelectableChannel) base.getSocketOptx(zmq.ZMQ.ZMQ_FD); } /** * The 'ZMQ_EVENTS' option shall retrieve event flags for the specified socket. * If a message can be read from the socket ZMQ_POLLIN flag is set. If message can * be written to the socket ZMQ_POLLOUT flag is set. * * @return the mask of outstanding events. */ public int getEvents() { return base.getSocketOpt(zmq.ZMQ.ZMQ_EVENTS); } /** * The 'ZMQ_SUBSCRIBE' option shall establish a new message filter on a 'ZMQ_SUB' socket. * Newly created 'ZMQ_SUB' sockets shall filter out all incoming messages, therefore you * should call this option to establish an initial message filter. *

* An empty 'option_value' of length zero shall subscribe to all incoming messages. A * non-empty 'option_value' shall subscribe to all messages beginning with the specified * prefix. Mutiple filters may be attached to a single 'ZMQ_SUB' socket, in which case a * message shall be accepted if it matches at least one filter. * * @param topic * @return true if the option was set, otherwise false */ public boolean subscribe(byte[] topic) { return setSocketOpt(zmq.ZMQ.ZMQ_SUBSCRIBE, topic); } /** * The 'ZMQ_SUBSCRIBE' option shall establish a new message filter on a 'ZMQ_SUB' socket. * Newly created 'ZMQ_SUB' sockets shall filter out all incoming messages, therefore you * should call this option to establish an initial message filter. *

* An empty 'option_value' of length zero shall subscribe to all incoming messages. A * non-empty 'option_value' shall subscribe to all messages beginning with the specified * prefix. Mutiple filters may be attached to a single 'ZMQ_SUB' socket, in which case a * message shall be accepted if it matches at least one filter. * * @param topic * @return true if the option was set, otherwise false */ public boolean subscribe(String topic) { return setSocketOpt(zmq.ZMQ.ZMQ_SUBSCRIBE, topic); } /** * The 'ZMQ_UNSUBSCRIBE' option shall remove an existing message filter on a 'ZMQ_SUB' * socket. The filter specified must match an existing filter previously established with * the 'ZMQ_SUBSCRIBE' option. If the socket has several instances of the same filter * attached the 'ZMQ_UNSUBSCRIBE' option shall remove only one instance, leaving the rest in * place and functional. * * @param topic * @return true if the option was set, otherwise false */ public boolean unsubscribe(byte[] topic) { return setSocketOpt(zmq.ZMQ.ZMQ_UNSUBSCRIBE, topic); } /** * The 'ZMQ_UNSUBSCRIBE' option shall remove an existing message filter on a 'ZMQ_SUB' * socket. The filter specified must match an existing filter previously established with * the 'ZMQ_SUBSCRIBE' option. If the socket has several instances of the same filter * attached the 'ZMQ_UNSUBSCRIBE' option shall remove only one instance, leaving the rest in * place and functional. * * @param topic * @return true if the option was set, otherwise false */ public boolean unsubscribe(String topic) { return setSocketOpt(zmq.ZMQ.ZMQ_UNSUBSCRIBE, topic); } /** * Joins a group. * Opposite action is {@link Socket#leave(String)} * @param group the name of the group to join. Limited to 16 characters. * @return true if the group was no already joined, otherwise false. */ public boolean join(String group) { assert ("DISH".equals(base.typeString())) : "Only DISH sockets can join a group"; return base.join(group); } /** * Leaves a group. * Opposite action is {@link Socket#join(String)} * @param group the name of the group to leave. Limited to 16 characters. * @return false if the group was not joined before, otherwise true. */ public boolean leave(String group) { assert ("DISH".equals(base.typeString())) : "Only DISH sockets can leave a group"; return base.leave(group); } /** * Set custom Encoder * * @param cls * @return true if the option was set, otherwise false */ @Deprecated public boolean setEncoder(Class cls) { return setSocketOpt(zmq.ZMQ.ZMQ_ENCODER, cls); } /** * Set custom Decoder * * @param cls * @return true if the option was set, otherwise false */ @Deprecated public boolean setDecoder(Class cls) { return setSocketOpt(zmq.ZMQ.ZMQ_DECODER, cls); } /** * Sets the limit threshold where messages of a given size will be allocated using Direct ByteBuffer. * It means that after this limit, there will be a slight penalty cost at the creation, * but the subsequent operations will be faster. * Set to 0 or negative to disable the threshold mechanism. * * @param threshold the threshold to set for the size limit of messages. 0 or negative to disable this system. * @return true if the option was set, otherwise false. */ public boolean setMsgAllocationHeapThreshold(int threshold) { return setSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD, threshold); } /** * Gets the limit threshold where messages of a given size will be allocated using Direct ByteBuffer. * It means that after this limit, there will be a slight penalty cost at the creation, * but the subsequent operations will be faster. * * @return the threshold */ public int getMsgAllocationHeapThreshold() { return base.getSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD); } /** * Sets a custom message allocator. * * @param allocator the custom allocator. * @return true if the option was set, otherwise false. */ public boolean setMsgAllocator(MsgAllocator allocator) { return setSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATOR, allocator); } /** * Set a custom {@link java.nio.channels.spi.SelectorProvider} chooser. * * @param chooser the custom chooser. * @return true if the option was set, otherwise false. */ public boolean setSelectorChooser(SelectorProviderChooser chooser) { return base.setSocketOpt(zmq.ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, chooser); } /** * Return the custom {@link java.nio.channels.spi.SelectorProvider} chooser. * * @return the {@link java.nio.channels.spi.SelectorProvider} chooser. */ public SelectorProviderChooser getSelectorProviderChooser() { return (SelectorProviderChooser) base.getSocketOptx(zmq.ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER); } /** * The ZMQ_CONNECT_RID option sets the peer id of the next host connected via the connect() call, * and immediately readies that connection for data transfer with the named id. * This option applies only to the first subsequent call to connect(), * calls thereafter use default connection behavior. * Typical use is to set this socket option ahead of each connect() attempt to a new host. * Each connection MUST be assigned a unique name. Assigning a name that is already in use is not allowed. * Useful when connecting ROUTER to ROUTER, or STREAM to STREAM, as it allows for immediate sending to peers. * Outbound id framing requirements for ROUTER and STREAM sockets apply. * The peer id should be from 1 to 255 bytes long and MAY NOT start with binary zero. * * @param rid the peer id of the next host. * @return true if the option was set, otherwise false. */ public boolean setConnectRid(String rid) { return setSocketOpt(zmq.ZMQ.ZMQ_CONNECT_RID, rid); } /** * The ZMQ_CONNECT_RID option sets the peer id of the next host connected via the connect() call, * and immediately readies that connection for data transfer with the named id. * This option applies only to the first subsequent call to connect(), * calls thereafter use default connection behavior. * Typical use is to set this socket option ahead of each connect() attempt to a new host. * Each connection MUST be assigned a unique name. Assigning a name that is already in use is not allowed. * Useful when connecting ROUTER to ROUTER, or STREAM to STREAM, as it allows for immediate sending to peers. * Outbound id framing requirements for ROUTER and STREAM sockets apply. * The peer id should be from 1 to 255 bytes long and MAY NOT start with binary zero. * * @param rid the peer id of the next host. * @return true if the option was set, otherwise false. */ public boolean setConnectRid(byte[] rid) { return setSocketOpt(zmq.ZMQ.ZMQ_CONNECT_RID, rid); } /** * Sets the raw mode on the ROUTER, when set to true. * When the ROUTER socket is in raw mode, and when using the tcp:// transport, * it will read and write TCP data without ØMQ framing. * This lets ØMQ applications talk to non-ØMQ applications. * When using raw mode, you cannot set explicit identities, * and the ZMQ_SNDMORE flag is ignored when sending data messages. * In raw mode you can close a specific connection by sending it a zero-length message (following the identity frame). * * @param raw true to set the raw mode on the ROUTER. * @return true if the option was set, otherwise false. */ public boolean setRouterRaw(boolean raw) { return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_RAW, raw); } /** * When set to true, the socket will automatically send * an empty message when a new connection is made or accepted. * You may set this on REQ, DEALER, or ROUTER sockets connected to a ROUTER socket. * The application must filter such empty messages. * The ZMQ_PROBE_ROUTER option in effect provides the ROUTER application with an event signaling the arrival of a new peer. * * @param probe true to send automatically an empty message when a new connection is made or accepted. * @return true if the option was set, otherwise false. */ public boolean setProbeRouter(boolean probe) { return setSocketOpt(zmq.ZMQ.ZMQ_PROBE_ROUTER, probe); } /** * Sets the ROUTER socket behavior when an unroutable message is encountered. * A value of false is the default and discards the message silently * when it cannot be routed or the peers SNDHWM is reached. * A value of true returns an EHOSTUNREACH error code if the message cannot be routed * or EAGAIN error code if the SNDHWM is reached and ZMQ_DONTWAIT was used. * Without ZMQ_DONTWAIT it will block until the SNDTIMEO is reached or a spot in the send queue opens up. * * @param mandatory A value of false is the default and discards the message silently when it cannot be routed. * A value of true returns an EHOSTUNREACH error code if the message cannot be routed. * @return true if the option was set, otherwise false. */ public boolean setRouterMandatory(boolean mandatory) { return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_MANDATORY, mandatory); } /** * If two clients use the same identity when connecting to a ROUTER, * the results shall depend on the ZMQ_ROUTER_HANDOVER option setting. * If that is not set (or set to the default of false), * the ROUTER socket shall reject clients trying to connect with an already-used identity. * If that option is set to true, the ROUTER socket shall hand-over the connection to the new client and disconnect the existing one. * * @param handover A value of false, (default) the ROUTER socket shall reject clients trying to connect with an already-used identity * A value of true, the ROUTER socket shall hand-over the connection to the new client and disconnect the existing one * @return true if the option was set, otherwise false. */ public boolean setRouterHandover(boolean handover) { return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_HANDOVER, handover); } /** * Sets the XPUB socket behavior on new subscriptions and unsubscriptions. * * @param verbose A value of false is the default and passes only new subscription messages to upstream. * A value of true passes all subscription messages upstream. * @return true if the option was set, otherwise false. */ public boolean setXpubVerbose(boolean verbose) { return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_VERBOSE, verbose); } /** * Sets the XPUB socket behaviour to return error EAGAIN if SENDHWM is reached and the message could not be send. * A value of false is the default and drops the message silently when the peers SNDHWM is reached. * A value of true returns an EAGAIN error code if the SNDHWM is reached and ZMQ_DONTWAIT was used. * * @param noDrop * @return true if the option was set, otherwise false. */ public boolean setXpubNoDrop(boolean noDrop) { return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_NODROP, noDrop); } public boolean setXpubManual(boolean manual) { return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_MANUAL, manual); } public boolean setXpubVerboser(boolean verboser) { return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_VERBOSER, verboser); } /** * @return the IPV4ONLY * @see #setIPv4Only (boolean) * @deprecated use {@link #isIPv6()} instead (inverted logic: ipv4 = true <==> ipv6 = false) */ @Deprecated public boolean getIPv4Only() { return !isIPv6(); } /** * Retrieve the IPv6 option for the socket. * A value of true means IPv6 is enabled on the socket, * while false means the socket will use only IPv4. * When IPv6 is enabled the socket will connect to, * or accept connections from, both IPv4 and IPv6 hosts. * * @return the IPV6 configuration. * @see #setIPv6 (boolean) */ public boolean isIPv6() { return (Boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_IPV6); } /** * Retrieve the IPv6 option for the socket. * A value of true means IPv6 is enabled on the socket, * while false means the socket will use only IPv4. * When IPv6 is enabled the socket will connect to, * or accept connections from, both IPv4 and IPv6 hosts. * * @return the IPV6 configuration. * @see #setIPv6 (boolean) */ public boolean getIPv6() { return isIPv6(); } /** * The 'ZMQ_IPV4ONLY' option shall set the underlying native socket type. * An IPv6 socket lets applications connect to and accept connections from both IPv4 and IPv6 hosts. * * @param v4only A value of true will use IPv4 sockets, while the value of false will use IPv6 sockets * @return true if the option was set, otherwise false * @deprecated use {@link #setIPv6(boolean)} instead (inverted logic: ipv4 = true <==> ipv6 = false) */ @Deprecated public boolean setIPv4Only(boolean v4only) { return setIPv6(!v4only); } /** *

Set the IPv6 option for the socket.

*

A value of true means IPv6 is enabled on the socket, while false means the socket will use only IPv4. * When IPv6 is enabled the socket will connect to, or accept connections from, both IPv4 and IPv6 hosts.

*

The default value is false, unless the following system properties are set:

*
    *
  • java.net.preferIPv4Stack=false
  • *
  • java.net.preferIPv6Addresses=true
  • *
* * @param v6 A value of true will use IPv6 sockets, while the value of false will use IPv4 sockets only * @return true if the option was set, otherwise false * @see #isIPv6() */ public boolean setIPv6(boolean v6) { return setSocketOpt(zmq.ZMQ.ZMQ_IPV6, v6); } /** * @return the keep alive setting. * @see #setTCPKeepAlive(int) */ public int getTCPKeepAlive() { return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE); } /** * Override SO_KEEPALIVE socket option (where supported by OS) to enable keep-alive packets for a socket * connection. Possible values are -1, 0, 1. The default value -1 will skip all overrides and do the OS default. * * @param optVal The value of 'ZMQ_TCP_KEEPALIVE' to turn TCP keepalives on (1) or off (0). * @return true if the option was set, otherwise false */ public boolean setTCPKeepAlive(int optVal) { return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE, optVal); } /** * @see #setDelayAttachOnConnect(boolean) * @deprecated use {@link #setImmediate(boolean)} instead (inverted logic: immediate = true <==> delay attach on connect = false) */ @Deprecated public boolean getDelayAttachOnConnect() { return !isImmediate(); } /** * Accept messages only when connections are made *

* If set to true, will delay the attachment of a pipe on connect until the underlying connection * has completed. This will cause the socket to block if there are no other connections, but will * prevent queues from filling on pipes awaiting connection * * @param value The value of 'ZMQ_DELAY_ATTACH_ON_CONNECT'. Default false. * @return true if the option was set * @deprecated use {@link #setImmediate(boolean)} instead (warning, the boolean is inverted) */ @Deprecated public boolean setDelayAttachOnConnect(boolean value) { return setImmediate(!value); } /** * Retrieve the state of the attach on connect value. * If false, will delay the attachment of a pipe on connect until the underlying connection has completed. * This will cause the socket to block if there are no other connections, but will prevent queues from filling on pipes awaiting connection. * * @see #setImmediate(boolean) */ public boolean isImmediate() { return (boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_IMMEDIATE); } /** * Retrieve the state of the attach on connect value. * If false, will delay the attachment of a pipe on connect until the underlying connection has completed. * This will cause the socket to block if there are no other connections, but will prevent queues from filling on pipes awaiting connection. * * @see #setImmediate(boolean) */ public boolean getImmediate() { return isImmediate(); } /** * Accept messages immediately or only when connections are made *

* By default queues will fill on outgoing connections even if the connection has not completed. * This can lead to "lost" messages on sockets with round-robin routing (REQ, PUSH, DEALER). * If this option is set to false, messages shall be queued only to completed connections. * This will cause the socket to block if there are no other connections, * but will prevent queues from filling on pipes awaiting connection. * * @param value The value of 'ZMQ_IMMEDIATE'. Default true. * @return true if the option was set, otherwise false. * @see #isImmediate() */ public boolean setImmediate(boolean value) { return setSocketOpt(zmq.ZMQ.ZMQ_IMMEDIATE, value); } /** * Sets the SOCKS5 proxy address that shall be used by the socket for the TCP connection(s). * Does not support SOCKS5 authentication. * If the endpoints are domain names instead of addresses they shall not be resolved * and they shall be forwarded unchanged to the SOCKS proxy service * in the client connection request message (address type 0x03 domain name). * * @param proxy * @return true if the option was set, otherwise false. * @see #getSocksProxy() */ public boolean setSocksProxy(String proxy) { return setSocketOpt(zmq.ZMQ.ZMQ_SOCKS_PROXY, proxy); } /** * Sets the SOCKS5 proxy address that shall be used by the socket for the TCP connection(s). * Does not support SOCKS5 authentication. * If the endpoints are domain names instead of addresses they shall not be resolved * and they shall be forwarded unchanged to the SOCKS proxy service * in the client connection request message (address type 0x03 domain name). * * @param proxy * @return true if the option was set, otherwise false. * @see #getSocksProxy() */ public boolean setSocksProxy(byte[] proxy) { return setSocketOpt(zmq.ZMQ.ZMQ_SOCKS_PROXY, proxy); } /** * The ZMQ_SOCKS_PROXY option shall retrieve the SOCKS5 proxy address in string format. * The returned value MAY be empty. * * @return the SOCKS5 proxy address in string format * @see #setSocksProxy(byte[]) */ public String getSocksProxy() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_SOCKS_PROXY); } /** * The ZMQ_LAST_ENDPOINT option shall retrieve the last endpoint bound for TCP and IPC transports. * The returned value will be a string in the form of a ZMQ DSN. * Note that if the TCP host is INADDR_ANY, indicated by a *, then the returned address will be 0.0.0.0 (for IPv4). */ public String getLastEndpoint() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_LAST_ENDPOINT); } /** * Sets the domain for ZAP (ZMQ RFC 27) authentication. * For NULL security (the default on all tcp:// connections), * ZAP authentication only happens if you set a non-empty domain. * For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present. * See http://rfc.zeromq.org/spec:27 for more details. * * @param domain the domain of ZAP authentication * @return true if the option was set * @see #getZapDomain() */ public boolean setZapDomain(String domain) { return setSocketOpt(zmq.ZMQ.ZMQ_ZAP_DOMAIN, domain); } /** * Sets the domain for ZAP (ZMQ RFC 27) authentication. * For NULL security (the default on all tcp:// connections), * ZAP authentication only happens if you set a non-empty domain. * For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present. * See http://rfc.zeromq.org/spec:27 for more details. * * @param domain the domain of ZAP authentication * @return true if the option was set * @see #getZapDomain() */ public boolean setZapDomain(byte[] domain) { return setSocketOpt(zmq.ZMQ.ZMQ_ZAP_DOMAIN, domain); } /** * The ZMQ_ZAP_DOMAIN option shall retrieve the last ZAP domain set for the socket. * The returned value MAY be empty. * * @return the domain of ZAP authentication * @see #setZapDomain(String) */ public String getZapDomain() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_ZAP_DOMAIN); } /** * Sets the domain for ZAP (ZMQ RFC 27) authentication. * For NULL security (the default on all tcp:// connections), * ZAP authentication only happens if you set a non-empty domain. * For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present. * See http://rfc.zeromq.org/spec:27 for more details. * * @param domain the domain of ZAP authentication * @return true if the option was set * @see #getZapDomain() */ public boolean setZAPDomain(String domain) { return setZapDomain(domain); } /** * Sets the domain for ZAP (ZMQ RFC 27) authentication. * For NULL security (the default on all tcp:// connections), * ZAP authentication only happens if you set a non-empty domain. * For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present. * See http://rfc.zeromq.org/spec:27 for more details. * * @param domain the domain of ZAP authentication * @return true if the option was set * @see #getZapDomain() */ public boolean setZAPDomain(byte[] domain) { return setZapDomain(domain); } /** * The ZMQ_ZAP_DOMAIN option shall retrieve the last ZAP domain set for the socket. * The returned value MAY be empty. * * @return the domain of ZAP authentication * @see #setZapDomain(String) */ public String getZAPDomain() { return getZapDomain(); } /** * The ZMQ_SELFADDR_PROPERTY_NAME option shall retrieve the metadata record used to store the self address. * The returned value MAY be null or empty. * * @return the meta record name * @see #setSelfAddressPropertyName(String) */ public String getSelfAddressPropertyName() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_SELFADDR_PROPERTY_NAME); } /** * Sets the field name where the self address will be stored. * If set to null or empty string, it will not be stored * * @param recordName the name of the field * @return true if the option was set * @see #getSelfAddressPropertyName() */ public boolean setSelfAddressPropertyName(String recordName) { return setSocketOpt(zmq.ZMQ.ZMQ_SELFADDR_PROPERTY_NAME, recordName); } /** * Defines whether the socket will act as server for PLAIN security, see zmq_plain(7). * A value of true means the socket will act as PLAIN server. * A value of false means the socket will not act as PLAIN server, * and its security role then depends on other option settings. * Setting this to false shall reset the socket security to NULL. * * @param server true if the role of the socket should be server for PLAIN security. * @return true if the option was set, otherwise false. * @see #isAsServerPlain() * @deprecated the naming is inconsistent with jzmq, please use {@link #setPlainServer(boolean)} instead */ @Deprecated public boolean setAsServerPlain(boolean server) { return setPlainServer(server); } /** * Defines whether the socket will act as server for PLAIN security, see zmq_plain(7). * A value of true means the socket will act as PLAIN server. * A value of false means the socket will not act as PLAIN server, * and its security role then depends on other option settings. * Setting this to false shall reset the socket security to NULL. * * @param server true if the role of the socket should be server for PLAIN security. * @return true if the option was set, otherwise false. * @see #isAsServerPlain() */ public boolean setPlainServer(boolean server) { return setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_SERVER, server); } /** * Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket. * * @return true if the role of the socket should be server for the PLAIN mechanism. * @see #setAsServerPlain(boolean) * @deprecated the naming is inconsistent with jzmq, please use {@link #getPlainServer()} instead */ @Deprecated public boolean isAsServerPlain() { return getPlainServer(); } /** * Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket. * * @return true if the role of the socket should be server for the PLAIN mechanism. * @see #setAsServerPlain(boolean) * @deprecated the naming is inconsistent with jzmq, please use {@link #getPlainServer()} instead */ @Deprecated public boolean getAsServerPlain() { return getPlainServer(); } /** * Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket. * * @return true if the role of the socket should be server for the PLAIN mechanism. * @see #setAsServerPlain(boolean) */ public boolean getPlainServer() { return (Boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_SERVER); } /** * Sets the username for outgoing connections over TCP or IPC. * If you set this to a non-null value, the security mechanism used for connections shall be PLAIN, see zmq_plain(7). * If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3). * * @param username the username to set. * @return true if the option was set, otherwise false. */ public boolean setPlainUsername(String username) { return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_USERNAME, username); } /** * Sets the password for outgoing connections over TCP or IPC. * If you set this to a non-null value, the security mechanism used for connections * shall be PLAIN, see zmq_plain(7). * If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3). * * @param password the password to set. * @return true if the option was set, otherwise false. */ public boolean setPlainPassword(String password) { return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_PASSWORD, password); } /** * Sets the username for outgoing connections over TCP or IPC. * If you set this to a non-null value, the security mechanism used for connections shall be PLAIN, see zmq_plain(7). * If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3). * * @param username the username to set. * @return true if the option was set, otherwise false. */ public boolean setPlainUsername(byte[] username) { return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_USERNAME, username); } /** * Sets the password for outgoing connections over TCP or IPC. * If you set this to a non-null value, the security mechanism used for connections * shall be PLAIN, see zmq_plain(7). * If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3). * * @param password the password to set. * @return true if the option was set, otherwise false. */ public boolean setPlainPassword(byte[] password) { return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_PASSWORD, password); } /** * The ZMQ_PLAIN_USERNAME option shall retrieve the last username * set for the PLAIN security mechanism. * * @return the plain username. */ public String getPlainUsername() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_USERNAME); } /** * The ZMQ_PLAIN_PASSWORD option shall retrieve the last password * set for the PLAIN security mechanism. * The returned value MAY be empty. * * @return the plain password. */ public String getPlainPassword() { return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_PASSWORD); } /** * Defines whether the socket will act as server for CURVE security, see zmq_curve(7). * A value of true means the socket will act as CURVE server. * A value of false means the socket will not act as CURVE server, * and its security role then depends on other option settings. * Setting this to false shall reset the socket security to NULL. * When you set this you must also set the server's secret key using the ZMQ_CURVE_SECRETKEY option. * A server socket does not need to know its own public key. * * @param server true if the role of the socket should be server for CURVE mechanism * @return true if the option was set * @see #isAsServerCurve() * @deprecated the naming is inconsistent with jzmq, please use {@link #setCurveServer(boolean)} instead */ @Deprecated public boolean setAsServerCurve(boolean server) { return setCurveServer(server); } /** * Defines whether the socket will act as server for CURVE security, see zmq_curve(7). * A value of true means the socket will act as CURVE server. * A value of false means the socket will not act as CURVE server, * and its security role then depends on other option settings. * Setting this to false shall reset the socket security to NULL. * When you set this you must also set the server's secret key using the ZMQ_CURVE_SECRETKEY option. * A server socket does not need to know its own public key. * * @param server true if the role of the socket should be server for CURVE mechanism * @return true if the option was set * @see #isAsServerCurve() */ public boolean setCurveServer(boolean server) { return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SERVER, server); } /** * Tells if the socket will act as server for CURVE security. * * @return true if the role of the socket should be server for CURVE mechanism. * @see #setAsServerCurve(boolean) * @deprecated the naming is inconsistent with jzmq, please use {@link #getCurveServer()} instead */ @Deprecated public boolean isAsServerCurve() { return getCurveServer(); } /** * Tells if the socket will act as server for CURVE security. * * @return true if the role of the socket should be server for CURVE mechanism. * @see #setAsServerCurve(boolean) */ public boolean getCurveServer() { return (boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SERVER); } /** * Tells if the socket will act as server for CURVE security. * * @return true if the role of the socket should be server for CURVE mechanism. * @see #setAsServerCurve(boolean) * @deprecated the naming is inconsistent with jzmq, please use {@link #getCurveServer()} instead */ @Deprecated public boolean getAsServerCurve() { return getCurveServer(); } /** * Sets the socket's long term public key. * You must set this on CURVE client sockets, see zmq_curve(7). * You can provide the key as 32 binary bytes, or as a 40-character string * encoded in the Z85 encoding format. * The public key must always be used with the matching secret key. * To generate a public/secret key pair, * use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}. * * @param key the curve public key * @return true if the option was set, otherwise false * @see #getCurvePublicKey() */ public boolean setCurvePublicKey(byte[] key) { return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_PUBLICKEY, key); } /** * Sets the socket's long term server key. * You must set this on CURVE client sockets, see zmq_curve(7). * You can provide the key as 32 binary bytes, or as a 40-character string * encoded in the Z85 encoding format. * This key must have been generated together with the server's secret key. * To generate a public/secret key pair, * use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}. * * @param key the curve server key * @return true if the option was set, otherwise false * @see #getCurveServerKey() */ public boolean setCurveServerKey(byte[] key) { return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SERVERKEY, key); } /** * Sets the socket's long term secret key. * You must set this on both CURVE client and server sockets, see zmq_curve(7). * You can provide the key as 32 binary bytes, or as a 40-character string * encoded in the Z85 encoding format. * To generate a public/secret key pair, * use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}. * * @param key the curve secret key * @return true if the option was set, otherwise false * @see #getCurveSecretKey() */ public boolean setCurveSecretKey(byte[] key) { return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SECRETKEY, key); } /** * Retrieves the current long term public key for the socket in binary format of 32 bytes. * * @return key the curve public key * @see #setCurvePublicKey(byte[]) */ public byte[] getCurvePublicKey() { return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_PUBLICKEY); } /** * Retrieves the current server key for the socket in binary format of 32 bytes. * * @return key the curve server key * @see #setCurveServerKey(byte[]) */ public byte[] getCurveServerKey() { return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SERVERKEY); } /** * Retrieves the current long term secret key for the socket in binary format of 32 bytes. * * @return key the curve secret key * @see #setCurveSecretKey(byte[]) */ public byte[] getCurveSecretKey() { return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SECRETKEY); } /** * The ZMQ_MECHANISM option shall retrieve the current security mechanism for the socket. * * @return the current mechanism. */ public Mechanism getMechanism() { return Mechanism.find((Mechanisms) base.getSocketOptx(zmq.ZMQ.ZMQ_MECHANISM)); } /** * When set, the socket will automatically send a hello message when a new connection is made or accepted. * You may set this on DEALER or ROUTER sockets. * The combination with ZMQ_HEARTBEAT_IVL is powerful and simplify protocols, * as now heartbeat and sending the hello message can be left out of protocols and be handled by zeromq. * * @param helloMsg * @return true if the option was set, otherwise false */ public boolean setHelloMsg(byte[] helloMsg) { return setSocketOpt(zmq.ZMQ.ZMQ_HELLO_MSG, helloMsg); } /** * Bind to network interface. Start listening for new connections. * * @param addr the endpoint to bind to. * @return true if the socket was bound, otherwise false. */ public boolean bind(String addr) { boolean rc = base.bind(addr); mayRaise(); return rc; } /** * Bind to network interface to a random port. Start listening for new * connections. * * @param addr the endpoint to bind to. */ public int bindToRandomPort(String addr) { return bindToRandomPort(addr, DYNFROM, DYNTO); } /** * Bind to network interface to a random port. Start listening for new * connections. * * @param addr the endpoint to bind to. * @param min The minimum port in the range of ports to try. * @param max The maximum port in the range of ports to try. */ public int bindToRandomPort(String addr, int min, int max) { int port; for (int i = 0; i < 100; i++) { // hardcoded to 100 tries. should this be parametrised port = zmq.util.Utils.randomInt(max - min + 1) + min; if (base.bind(String.format("%s:%s", addr, port))) { base.errno.set(0); return port; } // port++; } throw new ZMQException("Could not bind socket to random port.", ZError.EADDRINUSE); } /** * Connects the socket to an endpoint and then accepts incoming connections on that endpoint. *

* The endpoint is a string consisting of a transport :// followed by an address. *
* The transport specifies the underlying protocol to use. *
* The address specifies the transport-specific address to connect to. *

* ØMQ provides the the following transports: *

    *
  • tcp - unicast transport using TCP
  • *
  • ipc - local inter-process communication transport
  • *
  • inproc - local in-process (inter-thread) communication transport
  • *
* Every ØMQ socket type except ZMQ_PAIR supports one-to-many and many-to-one semantics. * The precise semantics depend on the socket type. *

* For most transports and socket types the connection is not performed immediately but as needed by ØMQ. *
* Thus a successful call to connect(String) does not mean that the connection was or could actually be established. *
* Because of this, for most transports and socket types * the order in which a server socket is bound and a client socket is connected to it does not matter. *
* The first exception is when using the inproc:// transport: you must call {@link #bind(String)} before calling connect(). *
* The second exception are ZMQ_PAIR sockets, which do not automatically reconnect to endpoints. *

* Following a connect(), for socket types except for ZMQ_ROUTER, the socket enters its normal ready state. *
* By contrast, following a {@link #bind(String)} alone, the socket enters a mute state * in which the socket blocks or drops messages according to the socket type. *
* A ZMQ_ROUTER socket enters its normal ready state for a specific peer * only when handshaking is complete for that peer, which may take an arbitrary time. * * @param addr the endpoint to connect to. * @return true if the socket was connected, otherwise false. */ public boolean connect(String addr) { boolean rc = base.connect(addr); mayRaise(); return rc; } /** * Disconnect from remote application. * * @param addr the endpoint to disconnect from. * @return true if successful. */ public boolean disconnect(String addr) { return base.termEndpoint(addr); } /** * Stop accepting connections on a socket. * * @param addr the endpoint to unbind from. * @return true if successful. */ public boolean unbind(String addr) { return base.termEndpoint(addr); } /** * create outgoing connection from socket and return the connection routing id in thread-safe and atomic way. * The function is supported only on the {@link SocketType#PEER} or {@link SocketType#RAW} socket types * and would return `0` with 'errno' set to 'ENOTSUP' otherwise. * @param addr the endpoint of the remote socket. * @return the endpoint routing ID. */ public int connectPeer(String addr) { return base.connectPeer(addr); } /** * Queues a message created from data, so it can be sent. * * @param msg the {@link Msg} to send. The message is either a single-part message by itself, * or the last part of a multi-part message. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean sendMsg(Msg msg) { return sendMsg(msg, 0); } /** * Queues a multi-part message created from data, so it can be sent. * * @param msg the message to send. further message parts are to follow. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean sendMsgMore(Msg msg) { return sendMsg(msg, zmq.ZMQ.ZMQ_SNDMORE); } /** * Queues a message created from data, so it can be sent. * * @param msg the {@link Msg} to send. The message is either a single-part message by itself, * or the last part of a multi-part message. * @param flags a combination (with + or |) of the flags defined below: *

    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean sendMsg(Msg msg, int flags) { if (base.send(msg, flags)) { return true; } mayRaise(); return false; } /** * Queues a message created from data, so it can be sent. * * @param data the data to send. The data is either a single-part message by itself, * or the last part of a multi-part message. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(String data) { return send(data.getBytes(CHARSET), 0); } /** * Queues a multi-part message created from data, so it can be sent. * * @param data the data to send. further message parts are to follow. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean sendMore(String data) { return send(data.getBytes(CHARSET), zmq.ZMQ.ZMQ_SNDMORE); } /** * Queues a message created from data. * * @param data the data to send. * @param flags a combination (with + or |) of the flags defined below: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(String data, int flags) { return send(data.getBytes(CHARSET), flags); } /** * Queues a message created from data, so it can be sent. * * @param data the data to send. The data is either a single-part message by itself, * or the last part of a multi-part message. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(byte[] data) { return send(data, 0); } /** * Queues a multi-part message created from data, so it can be sent. * * @param data the data to send. further message parts are to follow. * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean sendMore(byte[] data) { return send(data, zmq.ZMQ.ZMQ_SNDMORE); } /** * Queues a message created from data, so it can be sent. * * @param data the data to send. * @param flags a combination (with + or |) of the flags defined below: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(byte[] data, int flags) { zmq.Msg msg = new zmq.Msg(data); if (base.send(msg, flags)) { return true; } mayRaise(); return false; } /** * Queues a message created from data, so it can be sent, the call be canceled by calling cancellationToken {@link CancellationToken#cancel()}. * If the operation is canceled a ZMQException is thrown with error code set to {@link ZError#ECANCELED}. * @param data the data to send. * @param flags a combination (with + or |) of the flags defined below: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(byte[] data, int flags, CancellationToken cancellationToken) { zmq.Msg msg = new zmq.Msg(data); if (base.send(msg, flags, cancellationToken.canceled)) { return true; } mayRaise(); return false; } /** * Queues a message created from data, so it can be sent. * * @param data the data to send. * @param off the index of the first byte to be sent. * @param length the number of bytes to be sent. * @param flags a combination (with + or |) of the flags defined below: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. */ public boolean send(byte[] data, int off, int length, int flags) { byte[] copy = new byte[length]; System.arraycopy(data, off, copy, 0, length); zmq.Msg msg = new zmq.Msg(copy); if (base.send(msg, flags)) { return true; } mayRaise(); return false; } /** * Queues a message created from data, so it can be sent. * * @param data ByteBuffer payload * @param flags a combination (with + or |) of the flags defined below: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH}) * that block when there are no available peers (or all peers have full high-water mark), * specifies that the operation should be performed in non-blocking mode. * If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
  • *
  • {@link org.zeromq.ZMQ#SNDMORE SNDMORE}: * Specifies that the message being sent is a multi-part message, * and that further message parts are to follow.
  • *
  • 0 : blocking send of a single-part message or the last of a multi-part message
  • *
* @return the number of bytes queued, -1 on error */ public int sendByteBuffer(ByteBuffer data, int flags) { zmq.Msg msg = new zmq.Msg(data); if (base.send(msg, flags)) { return msg.size(); } mayRaise(); return -1; } /** * Queues a 'picture' message to the socket (or actor), so it can be sent. * * @param picture The picture is a string that defines the type of each frame. * This makes it easy to send a complex multiframe message in * one call. The picture can contain any of these characters, * each corresponding to zero or one arguments: * * * * * * * * * * * * *
Type of arguments
i = int (stores signed integer)
1 = byte (stores 8-bit unsigned integer)
2 = int (stores 16-bit unsigned integer)
4 = long (stores 32-bit unsigned integer)
8 = long (stores 64-bit unsigned integer)
s = String
b = byte[]
f = ZFrame
m = ZMsg (sends all frames in the ZMsg)
z = sends zero-sized frame (0 arguments)
* Note that s, b, f and m are encoded the same way and the choice is * offered as a convenience to the sender, which may or may not already * have data in a ZFrame or ZMsg. Does not change or take ownership of * any arguments. *

* Also see {@link #recvPicture(String)}} how to recv a * multiframe picture. * @param args Arguments according to the picture * @return true if successful, false if sending failed for any reason */ @Draft public boolean sendPicture(String picture, Object... args) { return new ZPicture().sendPicture(this, picture, args); } /** * Queues a binary encoded 'picture' message to the socket (or actor), so it can be sent. * This method is similar to {@link #sendPicture(String, Object...)}, except the arguments * are encoded in a binary format that is compatible with zproto, and is designed to reduce * memory allocations. * * @param picture The picture argument is a string that defines the * type of each argument. Supports these argument types: *

* * * * * * * * * * * * *
Type of arguments
patternjava typezproto type
1inttype = "number" size = "1"
2inttype = "number" size = "2"
4longtype = "number" size = "3"
8longtype = "number" size = "4"
sString, 0-255 charstype = "string"
SString, 0-2^32-1 charstype = "longstr"
cbyte[], 0-2^32-1 bytestype = "chunk"
fZFrametype = "frame"
mZMsgtype = "msg"
* @param args Arguments according to the picture * @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message. * This does not indicate that the message has been transmitted to the network. * @api.note Does not change or take ownership of any arguments. */ @Draft public boolean sendBinaryPicture(String picture, Object... args) { return new ZPicture().sendBinaryPicture(this, picture, args); } /** * Receives a message. * * @return the message received; null on error. */ public Msg recvMsg() { return recvMsg(0); } /** * Receives a message. *

* @param flags either: *

    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @return the message received; null on error. */ public Msg recvMsg(int flags) { zmq.Msg msg = base.recv(flags); if (msg != null) { return msg; } mayRaise(); return null; } /** * Receives a message. * * @return the message received, as an array of bytes; null on error. */ public byte[] recv() { return recv(0); } /** * Receives a message. *

* If possible, a reference to the data is returned, without copy. * Otherwise a new byte array will be allocated and the data will be copied. *

* @param flags either: *

    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @return the message received, as an array of bytes; null on error. */ public byte[] recv(int flags) { zmq.Msg msg = base.recv(flags); if (msg != null) { return msg.data(); } mayRaise(); return null; } /** * Receives a message, the call be canceled by calling cancellationToken {@link CancellationToken#cancel()}. * If the operation is canceled a ZMQException is thrown with error code set to {@link ZError#ECANCELED}. *

* If possible, a reference to the data is returned, without copy. * Otherwise a new byte array will be allocated and the data will be copied. *

* @param flags either: *

    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @param cancellationToken token to control cancellation of the receive operation. * The token can be created by calling {@link #createCancellationToken() }. * @return the message received, as an array of bytes; null on error. */ public byte[] recv(int flags, CancellationToken cancellationToken) { zmq.Msg msg = base.recv(flags, cancellationToken.canceled); if (msg != null) { return msg.data(); } mayRaise(); return null; } /** * Receives a message in to a specified buffer. * * @param buffer byte[] to copy zmq message payload in to. * @param offset offset in buffer to write data * @param len max bytes to write to buffer. * If len is smaller than the incoming message size, * the message will be truncated. * @param flags either: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @return the number of bytes read, -1 on error */ public int recv(byte[] buffer, int offset, int len, int flags) { zmq.Msg msg = base.recv(flags); if (msg != null) { return msg.getBytes(0, buffer, offset, len); } return -1; } /** * Receives a message into the specified ByteBuffer. * * @param buffer the buffer to copy the zmq message payload into * @param flags either: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @return the number of bytes read, -1 on error */ public int recvByteBuffer(ByteBuffer buffer, int flags) { zmq.Msg msg = base.recv(flags); if (msg != null) { buffer.put(msg.buf()); return msg.size(); } mayRaise(); return -1; } /** * @return the message received, as a String object; null on no message. */ public String recvStr() { return recvStr(0); } /** * Receives a message as a string. * * @param flags either: *
    *
  • {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}: * Specifies that the operation should be performed in non-blocking mode. * If there are no messages available on the specified socket, * the method shall fail with errno set to EAGAIN and return null.
  • *
  • 0 : receive operation blocks until one message is successfully retrieved, * or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
  • *
* @return the message received, as a String object; null on no message. */ public String recvStr(int flags) { byte[] msg = recv(flags); if (msg != null) { return new String(msg, CHARSET); } return null; } /** * Receive a 'picture' message to the socket (or actor). * * * @param picture The picture is a string that defines the type of each frame. * This makes it easy to recv a complex multiframe message in * one call. The picture can contain any of these characters, * each corresponding to zero or one elements in the result: * *

* * * * * * * * * * * * *
Type of arguments
i = int (stores signed integer)
1 = int (stores 8-bit unsigned integer)
2 = int (stores 16-bit unsigned integer)
4 = long (stores 32-bit unsigned integer)
8 = long (stores 64-bit unsigned integer)
s = String
b = byte[]
f = ZFrame (creates zframe)
m = ZMsg (creates a zmsg with the remaing frames)
z = null, asserts empty frame (0 arguments)
* * Also see {@link #sendPicture(String, Object...)} how to send a * multiframe picture. * * @return the picture elements as object array */ @Draft public Object[] recvPicture(String picture) { return new ZPicture().recvPicture(this, picture); } /** * Receive a binary encoded 'picture' message from the socket (or actor). * This method is similar to {@link #recv()}, except the arguments are encoded * in a binary format that is compatible with zproto, and is designed to * reduce memory allocations. * * @param picture The picture argument is a string that defines * the type of each argument. See {@link #sendBinaryPicture(String, Object...)} * for the supported argument types. * @return the picture elements as object array **/ @Draft public Object[] recvBinaryPicture(final String picture) { return new ZPicture().recvBinaryPicture(this, picture); } /** * Start a monitoring socket where events can be received. *

* Lets an application thread track socket events (like connects) on a ZeroMQ socket. * Each call to this method creates a {@link ZMQ#PAIR} socket and binds that to the specified inproc:// endpoint. * To collect the socket events, you must create your own PAIR socket, and connect that to the endpoint. *
* Supports only connection-oriented transports, that is, TCP, IPC. * * @param addr the endpoint to receive events from. (must be inproc transport) * @param events the events of interest. A bitmask of the socket events you wish to monitor. To monitor all events, use the event value {@link ZMQ#EVENT_ALL}. * @return true if monitor socket setup is successful * @throws ZMQException */ public boolean monitor(String addr, int events) { return base.monitor(addr, events); } /** * Register a custom event consumer. * * @param consumer The event consumer. * @param events the events of interest. A bitmask of the socket events you wish to monitor. To monitor all events, use the event value {@link ZMQ#EVENT_ALL}. * @return true if consumer setup is successful * @throws ZMQException */ public boolean setEventHook(ZEvent.ZEventConsummer consumer, int events) { return base.setEventHook(consumer, events); } protected void mayRaise() { int errno = base.errno(); if (errno != 0 && errno != zmq.ZError.EAGAIN) { throw new ZMQException(errno); } } public int errno() { return base.errno(); } @Override public String toString() { return base.toString(); } public enum Mechanism { NULL(Mechanisms.NULL), PLAIN(Mechanisms.PLAIN), CURVE(Mechanisms.CURVE); // TODO add GSSAPI once it is implemented private final Mechanisms mech; Mechanism(Mechanisms zmq) { this.mech = zmq; } private static Mechanism find(Mechanisms mech) { for (Mechanism candidate : values()) { if (candidate.mech == mech) { return candidate; } } return null; } } /** * Create a {@link CancellationToken} to cancel send/receive operations for this socket. * @return a new cancellation token associated with this socket. */ public CancellationToken createCancellationToken() { return new CancellationToken(base); } } /** * Provides a mechanism for applications to multiplex input/output events in a level-triggered fashion over a set of sockets */ public static class Poller implements Closeable { /** * For ØMQ sockets, at least one message may be received from the socket without blocking. *
* For standard sockets this is equivalent to the POLLIN flag of the poll() system call * and generally means that at least one byte of data may be read from fd without blocking. */ public static final int POLLIN = zmq.ZMQ.ZMQ_POLLIN; /** * For ØMQ sockets, at least one message may be sent to the socket without blocking. *
* For standard sockets this is equivalent to the POLLOUT flag of the poll() system call * and generally means that at least one byte of data may be written to fd without blocking. */ public static final int POLLOUT = zmq.ZMQ.ZMQ_POLLOUT; /** * For standard sockets, this flag is passed through {@link zmq.ZMQ#poll(Selector, zmq.poll.PollItem[], long)} to the underlying poll() system call * and generally means that some sort of error condition is present on the socket specified by fd. *
* For ØMQ sockets this flag has no effect if set in events, and shall never be returned in revents by {@link zmq.ZMQ#poll(Selector, zmq.poll.PollItem[], long)}. */ public static final int POLLERR = zmq.ZMQ.ZMQ_POLLERR; private static final int SIZE_DEFAULT = 32; private static final int SIZE_INCREMENT = 16; private final Selector selector; private final Context context; private final List items; private long timeout; /** * Class constructor. * * @param context a 0MQ context previously created. * @param size the number of Sockets this poller will contain. */ protected Poller(Context context, int size) { assert (context != null); this.context = context; selector = context.selector(); assert (selector != null); items = new ArrayList<>(size); timeout = -1L; } /** * Class constructor. * * @param context a 0MQ context previously created. */ protected Poller(Context context) { this(context, SIZE_DEFAULT); } @Override public void close() { context.close(selector); } /** * Register a Socket for polling on all events. * * @param socket the Socket we are registering. * @return the index identifying this Socket in the poll set. */ public int register(Socket socket) { return register(socket, POLLIN | POLLOUT | POLLERR); } /** * Register a Channel for polling on all events. * * @param channel the Channel we are registering. * @return the index identifying this Channel in the poll set. */ public int register(SelectableChannel channel) { return register(channel, POLLIN | POLLOUT | POLLERR); } /** * Register a Socket for polling on the specified events. *

* Automatically grow the internal representation if needed. * * @param socket the Socket we are registering. * @param events a mask composed by XORing POLLIN, POLLOUT and POLLERR. * @return the index identifying this Socket in the poll set. */ public int register(Socket socket, int events) { return registerInternal(new PollItem(socket, events)); } /** * Register a Socket for polling on the specified events. *

* Automatically grow the internal representation if needed. * * @param channel the Channel we are registering. * @param events a mask composed by XORing POLLIN, POLLOUT and POLLERR. * @return the index identifying this Channel in the poll set. */ public int register(SelectableChannel channel, int events) { return registerInternal(new PollItem(channel, events)); } /** * Register a Channel for polling on the specified events. *

* Automatically grow the internal representation if needed. * * @param item the PollItem we are registering. * @return the index identifying this Channel in the poll set. */ public int register(PollItem item) { return registerInternal(item); } /** * Register a Socket for polling on the specified events. *

* Automatically grow the internal representation if needed. * * @param item the PollItem we are registering. * @return the index identifying this Socket in the poll set. */ private int registerInternal(PollItem item) { items.add(item); return items.size() - 1; } /** * Unregister a Socket for polling on the specified events. * * @param socket the Socket to be unregistered */ public void unregister(Socket socket) { unregisterInternal(socket); } /** * Unregister a Socket for polling on the specified events. * * @param channel the Socket to be unregistered */ public void unregister(SelectableChannel channel) { unregisterInternal(channel); } /** * Unregister a Socket for polling on the specified events. * * @param socket the Socket to be unregistered */ private void unregisterInternal(Object socket) { items.removeIf(item -> item.socket == socket || item.getRawSocket() == socket); } /** * Get the PollItem associated with an index. * * @param index the desired index. * @return the PollItem associated with that index (or null). */ public PollItem getItem(int index) { if (index < 0 || index >= items.size()) { return null; } return this.items.get(index); } /** * Get the socket associated with an index. * * @param index the desired index. * @return the Socket associated with that index (or null). */ public Socket getSocket(int index) { if (index < 0 || index >= items.size()) { return null; } return items.get(index).socket; } /** * Get the current poll timeout. * * @return the current poll timeout in milliseconds. * @deprecated Timeout handling has been moved to the poll() methods. */ @Deprecated public long getTimeout() { return this.timeout; } /** * Set the poll timeout. * * @param timeout the desired poll timeout in milliseconds. * @deprecated Timeout handling has been moved to the poll() methods. */ @Deprecated public void setTimeout(long timeout) { if (timeout >= -1L) { this.timeout = timeout; } } /** * Get the current poll set size. * * @return the current poll set size. */ public int getSize() { return items.size(); } /** * Get the index for the next position in the poll set size. * * @deprecated use getSize instead * @return the index for the next position in the poll set size. */ @Deprecated public int getNext() { return items.size(); } /** * Issue a poll call. If the poller's internal timeout value * has been set, use that value as timeout; otherwise, block * indefinitely. * * @return how many objects where signaled by poll (). */ public int poll() { long tout = -1L; if (this.timeout > -1L) { tout = this.timeout; } return poll(tout); } /** * Issue a poll call, using the specified timeout value. *

* Since ZeroMQ 3.0, the timeout parameter is in milliseconds, * but prior to this the unit was microseconds. * * @param tout the timeout, as per zmq_poll (); * if -1, it will block indefinitely until an event * happens; if 0, it will return immediately; * otherwise, it will wait for at most that many * milliseconds/microseconds (see above). * @return how many objects where signaled by poll () * @see "http://api.zeromq.org/3-0:zmq-poll" */ public int poll(long tout) { if (tout < -1) { return 0; } if (items.isEmpty()) { return 0; } zmq.poll.PollItem[] pollItems = new zmq.poll.PollItem[items.size()]; for (int i = 0, j = 0; i < items.size(); i++) { if (items.get(i) != null) { pollItems[j++] = items.get(i).base; } } try { return zmq.ZMQ.poll(selector, pollItems, items.size(), tout); } catch (ZError.IOException e) { if (context.isTerminated()) { return 0; } else { throw (e); } } } /** * Check whether the specified element in the poll set was signaled for input. * * @param index of element * @return true if the element was signaled. */ public boolean pollin(int index) { if (index < 0 || index >= items.size()) { return false; } return items.get(index).isReadable(); } /** * Check whether the specified element in the poll set was signaled for output. * * @param index of element * @return true if the element was signaled. */ public boolean pollout(int index) { if (index < 0 || index >= items.size()) { return false; } return items.get(index).isWritable(); } /** * Check whether the specified element in the poll set was signaled for error. * * @param index of element * @return true if the element was signaled. */ public boolean pollerr(int index) { if (index < 0 || index >= items.size()) { return false; } return items.get(index).isError(); } } public static class PollItem { private final zmq.poll.PollItem base; private final Socket socket; public PollItem(Socket socket, int ops) { this.socket = socket; base = new zmq.poll.PollItem(socket.base, ops); } public PollItem(SelectableChannel channel, int ops) { base = new zmq.poll.PollItem(channel, ops); socket = null; } final zmq.poll.PollItem base() { return base; } public final SelectableChannel getRawSocket() { return base.getRawSocket(); } public final Socket getSocket() { return socket; } public final boolean isReadable() { return base.isReadable(); } public final boolean isWritable() { return base.isWritable(); } public final boolean isError() { return base.isError(); } public final int readyOps() { return base.readyOps(); } @Override public int hashCode() { return base.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof PollItem)) { return false; } PollItem target = (PollItem) obj; if (socket != null && socket == target.socket) { return true; } return getRawSocket() != null && getRawSocket() == target.getRawSocket(); } } /** * Inner class: Event. * Monitor socket event class * @deprecated Uses {@link org.zeromq.ZEvent} instead */ @Deprecated public static class Event { private final int event; // To keep backward compatibility, the old value field only store integer // The resolved value (Error, channel or other) is stored in resolvedValue field. private final Object value; private final Object resolvedValue; private final String address; public Event(int event, Object value, String address) { this.event = event; this.value = value; this.address = address; this.resolvedValue = value; } private Event(int event, Object value, Object resolvedValue, String address) { this.event = event; this.value = value; this.address = address; this.resolvedValue = resolvedValue; } /** * Receive an event from a monitor socket. * * @param socket the socket * @param flags the flags to apply to the receive operation. * @return the received event or null if no message was received. * @throws ZMQException */ public static Event recv(Socket socket, int flags) { zmq.ZMQ.Event e = zmq.ZMQ.Event.read(socket.base, flags); if (e == null) { return null; } Object resolvedValue; switch (e.event) { case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: resolvedValue = ZMonitor.ProtocolCode.findByCode((Integer) e.arg); break; case zmq.ZMQ.ZMQ_EVENT_CLOSE_FAILED: case zmq.ZMQ.ZMQ_EVENT_ACCEPT_FAILED: case zmq.ZMQ.ZMQ_EVENT_BIND_FAILED: case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: resolvedValue = Error.findByCode((Integer) e.arg); break; case zmq.ZMQ.ZMQ_EVENT_CONNECTED: case zmq.ZMQ.ZMQ_EVENT_LISTENING: case zmq.ZMQ.ZMQ_EVENT_ACCEPTED: case zmq.ZMQ.ZMQ_EVENT_CLOSED: case zmq.ZMQ.ZMQ_EVENT_DISCONNECTED: resolvedValue = e.getChannel(socket.base); break; case zmq.ZMQ.ZMQ_EVENT_CONNECT_DELAYED: case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED: case zmq.ZMQ.ZMQ_EVENT_MONITOR_STOPPED: resolvedValue = null; break; default: resolvedValue = e.arg; } return new Event(e.event, e.arg, resolvedValue, e.addr); } /** * Receive an event from a monitor socket. * Does a blocking recv. * * @param socket the socket * @return the received event. * @throws ZMQException */ public static Event recv(Socket socket) { return Event.recv(socket, 0); } public int getEvent() { return event; } public Object getValue() { return value; } public String getAddress() { return address; } /** * Used to check if the event is an error. *

* Generally, any event that define the errno is * considered as an error. * @return true if the evant was an error */ public boolean isError() { switch (event) { case EVENT_CLOSE_FAILED: case EVENT_ACCEPT_FAILED: case EVENT_BIND_FAILED: case HANDSHAKE_FAILED_PROTOCOL: case HANDSHAKE_FAILED_NO_DETAIL: return true; default: return false; } } /** * Used to check if the event is a warning. *

* Generally, any event that return an authentication failure is * considered as a warning. * @return */ public boolean isWarn() { switch (event) { case HANDSHAKE_FAILED_AUTH: return true; default: return false; } } /** * Return the argument as an integer or a Enum of the appropriate type if available. *

* It returns objects of type: *

    *
  • {@link org.zeromq.ZMonitor.ProtocolCode} for a handshake protocol error.
  • *
  • {@link org.zeromq.ZMQ.Error} for any other error.
  • *
  • {@link java.lang.Integer} when available.
  • *
  • null when no relevant value available.
  • *
* @param The expected type of the returned object * @return The resolved value. */ @SuppressWarnings("unchecked") public M resolveValue() { return (M) resolvedValue; } } /** * Class that interfaces the generation of CURVE key pairs. * *

The CURVE mechanism defines a mechanism for secure authentication and confidentiality for communications between a client and a server. * CURVE is intended for use on public networks. * The CURVE mechanism is defined by this document: http://rfc.zeromq.org/spec:25.

* *

Client and server roles

* *

A socket using CURVE can be either client or server, at any moment, but not both. The role is independent of bind/connect direction. * A socket can change roles at any point by setting new options. The role affects all connect and bind calls that follow it.

* *

To become a CURVE server, the application sets the {@link ZMQ.Socket#setAsServerCurve(boolean)} option on the socket, * and then sets the {@link ZMQ.Socket#setCurveSecretKey(byte[])} option to provide the socket with its long-term secret key. * The application does not provide the socket with its long-term public key, which is used only by clients.

* *

To become a CURVE client, the application sets the {@link ZMQ.Socket#setCurveServerKey(byte[])} option * with the long-term public key of the server it intends to connect to, or accept connections from, next. * The application then sets the {@link ZMQ.Socket#setCurvePublicKey(byte[])} and {@link ZMQ.Socket#setCurveSecretKey(byte[])} options with its client long-term key pair. * If the server does authentication it will be based on the client's long term public key.

* *

Key encoding

* *

The standard representation for keys in source code is either 32 bytes of base 256 (binary) data, * or 40 characters of base 85 data encoded using the Z85 algorithm defined by http://rfc.zeromq.org/spec:32. * The Z85 algorithm is designed to produce printable key strings for use in configuration files, the command line, and code. * There is a reference implementation in C at https://github.com/zeromq/rfc/tree/master/src.

* *

Test key values

* *

For test cases, the client shall use this long-term key pair (specified as hexadecimal and in Z85):

*
    *
  • public: *

    BB88471D65E2659B30C55A5321CEBB5AAB2B70A398645C26DCA2B2FCB43FC518

    *

    {@code Yne@$w-vo *

  • *
  • secret: *

    7BB864B489AFA3671FBE69101F94B38972F24816DFB01B51656B3FEC8DFD0888

    *

    {@code D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs}

    *
  • *
* *

And the server shall use this long-term key pair (specified as hexadecimal and in Z85):

*
    *
  • public: *

    54FCBA24E93249969316FB617C872BB0C1D1FF14800427C594CBFACF1BC2D652

    *

    {@code rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7}

    *
  • *
  • secret: *

    8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7

    *

    {@code JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6}

    *
  • *
*/ public static class Curve { public static final int KEY_SIZE = Options.CURVE_KEYSIZE; public static final int KEY_SIZE_Z85 = Options.CURVE_KEYSIZE_Z85; /** *

Returns a newly generated random keypair consisting of a public key * and a secret key.

* *

The keys are encoded using {@link #z85Encode}.

* * @return Randomly generated {@link KeyPair} */ public static KeyPair generateKeyPair() { String[] keys = new zmq.io.mechanism.curve.Curve().keypairZ85(); return new KeyPair(keys[0], keys[1]); } /** *

The function shall decode given key encoded as Z85 string into byte array.

*

The length of string shall be divisible by 5.

*

The decoding shall follow the ZMQ RFC 32 specification.

* * @param key Key to be decoded * @return The resulting key as byte array */ public static byte[] z85Decode(String key) { return Z85.decode(key); } /** *

Encodes the binary block specified by data into a string.

*

The size of the binary block must be divisible by 4.

*

A 32-byte CURVE key is encoded as 40 ASCII characters plus a null terminator.

*

The function shall encode the binary block specified into a string.

*

The encoding shall follow the ZMQ RFC 32 specification.

* * @param key Key to be encoded * @return The resulting key as String in Z85 */ public static String z85Encode(byte[] key) { return zmq.io.mechanism.curve.Curve.z85EncodePublic(key); } /** * A container for a public and a corresponding secret key. * Keys have to be encoded in Z85 format. */ public static class KeyPair { /** * Z85-encoded public key. */ public final String publicKey; /** * Z85-encoded secret key. */ public final String secretKey; public KeyPair(final String publicKey, final String secretKey) { Utils.checkArgument(publicKey != null, "Public key cannot be null"); Utils.checkArgument(publicKey.length() == Curve.KEY_SIZE_Z85, "Public key has to be Z85 format"); Utils.checkArgument(secretKey == null || secretKey.length() == Curve.KEY_SIZE_Z85, "Secret key has to be null or in Z85 format"); this.publicKey = publicKey; this.secretKey = secretKey; } } } /** * A cancellation token that allows canceling ongoing Socket send/receive operations. * When calling send/receive you can provide the cancellation token as an additional parameter. * To create a cancellation token call {@link Socket#createCancellationToken()}. * */ public static class CancellationToken { protected final AtomicBoolean canceled; final SocketBase socket; protected CancellationToken(SocketBase socket) { this.socket = socket; canceled = new AtomicBoolean(false); } public boolean isCancellationRequested() { return canceled.get(); } /** * Reset the cancellation token in order to reuse the token with another send/receive call. */ public void reset() { canceled.set(false); } /** * Cancel a pending the send/receive operation. */ public void cancel() { socket.cancel(canceled); } } } jeromq-0.6.0/src/main/java/org/zeromq/ZMQException.java000066400000000000000000000013351455771126300227600ustar00rootroot00000000000000package org.zeromq; import zmq.ZError; public class ZMQException extends UncheckedZMQException { private static final long serialVersionUID = -978820750094924644L; private final int code; public ZMQException(int errno) { super("Errno " + errno); code = errno; } public ZMQException(String message, int errno) { super(message); code = errno; } public ZMQException(String message, int errno, Throwable cause) { super(message, cause); code = errno; } public int getErrorCode() { return code; } @Override public String toString() { return super.toString() + " : " + ZError.toString(code); } } jeromq-0.6.0/src/main/java/org/zeromq/ZMQQueue.java000066400000000000000000000012751455771126300221110ustar00rootroot00000000000000package org.zeromq; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; public class ZMQQueue implements Runnable { private final Socket inSocket; private final Socket outSocket; /** * Class constructor. * * @param context * a 0MQ context previously created. * @param inSocket * input socket * @param outSocket * output socket */ public ZMQQueue(Context context, Socket inSocket, Socket outSocket) { this.inSocket = inSocket; this.outSocket = outSocket; } @Override public void run() { zmq.ZMQ.proxy(inSocket.base(), outSocket.base(), null); } } jeromq-0.6.0/src/main/java/org/zeromq/ZMonitor.java000066400000000000000000000445711455771126300222240ustar00rootroot00000000000000package org.zeromq; import java.io.Closeable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.zeromq.ZMQ.Socket; import org.zeromq.proto.ZNeedle; import zmq.util.Objects; /** * The ZMonitor actor provides an API for obtaining socket events such as * connected, listen, disconnected, etc. Socket events are only available * for sockets connecting or bound to ipc:// and tcp:// endpoints. */ public class ZMonitor implements Closeable { /** * High-level representation of an event. */ public static final class ZEvent { // Enum.values return a different array on each call, so keep it to avoid useless allocations private static final Event[] EVENTVALUES = Event.values(); /** * The type of the event. (never null) */ public final Event type; /** * The numerical code of the event. (useful in case of unrecognized event, in which case type is ALL) */ public final int code; /** * The address of the source of the event. */ public final String address; /** * String representation of the event value. * Nature of the value depends on the type of event. May be null */ public final String value; private ZEvent(ZMsg msg) { assert (msg != null); assert (msg.size() == 1 || msg.size() == 2) : msg.size(); final ZFrame frame = msg.pop(); final ZNeedle needle = new ZNeedle(frame); type = EVENTVALUES[needle.getNumber1()]; code = needle.getNumber4(); address = needle.getString(); if (msg.isEmpty()) { value = null; } else { value = msg.popString(); } } @SuppressWarnings("deprecation") public ZEvent(ZMQ.Event event) { code = event.getEvent(); address = event.getAddress(); type = Event.findByCode(code); Object tryValue = event.resolveValue(); if (tryValue != null) { value = tryValue.toString(); } else { value = null; } } @Override public String toString() { return "ZEvent [type=" + type + ", code=" + code + ", address=" + address + ", value=" + value + "]"; } } /** * Enumerates types of monitoring events. */ public enum Event { CONNECTED(ZMQ.EVENT_CONNECTED), CONNECT_DELAYED(ZMQ.EVENT_CONNECT_DELAYED), CONNECT_RETRIED(ZMQ.EVENT_CONNECT_RETRIED), LISTENING(ZMQ.EVENT_LISTENING), BIND_FAILED(ZMQ.EVENT_BIND_FAILED), ACCEPTED(ZMQ.EVENT_ACCEPTED), ACCEPT_FAILED(ZMQ.EVENT_ACCEPT_FAILED), CLOSED(ZMQ.EVENT_CLOSED), CLOSE_FAILED(ZMQ.EVENT_CLOSE_FAILED), DISCONNECTED(ZMQ.EVENT_DISCONNECTED), MONITOR_STOPPED(ZMQ.EVENT_MONITOR_STOPPED), HANDSHAKE_FAILED_NO_DETAIL(ZMQ.HANDSHAKE_FAILED_NO_DETAIL), HANDSHAKE_SUCCEEDED(ZMQ.HANDSHAKE_SUCCEEDED), HANDSHAKE_FAILED_PROTOCOL(ZMQ.HANDSHAKE_FAILED_PROTOCOL), HANDSHAKE_FAILED_AUTH(ZMQ.HANDSHAKE_FAILED_AUTH), HANDSHAKE_PROTOCOL(ZMQ.EVENT_HANDSHAKE_PROTOCOL), ALL(ZMQ.EVENT_ALL); private static final Map MAP = new HashMap<>(Event.values().length); static { for (Event e : Event.values()) { MAP.put(e.code, e); } } private final int code; Event(int code) { this.code = code; } /** * Find the {@link Event} associated with the numerical event code. * @param event the numerical event code * @return the found {@link Event} */ public static Event findByCode(int event) { return MAP.getOrDefault(event, ALL); } } /** * The code returned by handshake events, as generated by eventHandshakeXXX. * */ public enum ProtocolCode { ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE), ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY), ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME), ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA), ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC), ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH), ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED), ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY), ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID), ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION), ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE), ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA), ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED); private static final Map MAP = new HashMap<>(ProtocolCode.values().length); static { for (ProtocolCode e : ProtocolCode.values()) { MAP.put(e.code, e); } } private final int code; ProtocolCode(int code) { this.code = code; } /** * Find the {@link ProtocolCode} associated with the numerical error code send with eventHandshakeXXX. * @param code the numerical error code * @return the found {@link ProtocolCode} */ public static ProtocolCode findByCode(int code) { if (MAP.containsKey(code)) { return MAP.get(code); } else { throw new IllegalArgumentException("Protocol code unknown: " + code); } } } private boolean started; /** * Starts the monitoring. * Event types have to be added before the start, or they will take no effect. * @return this instance. */ public final ZMonitor start() { if (started) { System.out.println("ZMonitor: Unable to start while already started."); return this; } agent.send(START); agent.recv(); started = true; return this; } /** * Stops the monitoring and closes the actor. * When returning from that call, ZMonitor will be no more active. */ @Override public final void close() { destroy(); } /** * Stops the monitoring and closes the actor. * When returning from that call, ZMonitor will be no more active. */ public final void destroy() { agent.send(CLOSE); exit.awaitSilent(); agent.close(); } /** * Sets verbosity of the monitor. * @param verbose true for monitor to be verbose, otherwise false. * @return this instance. */ public final ZMonitor verbose(boolean verbose) { if (started) { System.out.println("ZMonitor: Unable to change verbosity while already started."); return this; } agent.send(VERBOSE, true); agent.send(Boolean.toString(verbose)); agent.recv(); return this; } /** * Adds event types to monitor. * @param events the types of events to monitor. * @return this instance. */ public final ZMonitor add(Event... events) { if (started) { System.out.println("ZMonitor: Unable to add events while already started."); return this; } ZMsg msg = new ZMsg(); msg.add(ADD_EVENTS); for (Event evt : events) { msg.add(evt.name()); } agent.send(msg); agent.recv(); return this; } /** * Removes event types from monitor. * @param events the types of events to stop monitoring. * @return this instance. */ public final ZMonitor remove(Event... events) { if (started) { System.out.println("ZMonitor: Unable to remove events while already started."); return this; } ZMsg msg = new ZMsg(); msg.add(REMOVE_EVENTS); for (Event evt : events) { msg.add(evt.name()); } agent.send(msg); agent.recv(); return this; } /** * Gets the next event, blocking for it until available. * @return the next monitored event or null if closed. */ public final ZEvent nextEvent() { return nextEvent(true); } /** * Gets the next event, blocking for it until available if requested. * @param wait true to block until next event is available, false to immediately return with null if there is no event. * @return the next event or null if there is currently none. */ public final ZEvent nextEvent(boolean wait) { if (!started) { System.out.println("ZMonitor: Start before getting events."); return null; } ZMsg msg = agent.recv(wait); if (msg == null) { return null; } return new ZEvent(msg); } /** * Gets the next event, blocking for it until available if requested. * @param timeout the time in milliseconds to wait for a message before returning null, -1 to block. * @return the next event or null if there is currently none after the specified timeout. */ public final ZEvent nextEvent(int timeout) { if (!started) { System.out.println("ZMonitor: Start before getting events."); return null; } ZMsg msg = agent.recv(timeout); if (msg == null) { return null; } return new ZEvent(msg); } private static final String START = "START"; private static final String CLOSE = "CLOSE"; private static final String VERBOSE = "VERBOSE"; private static final String ADD_EVENTS = "ADD_EVENTS"; private static final String REMOVE_EVENTS = "REMOVE_EVENTS"; private final ZAgent agent; private final ZStar.Exit exit; /** * Creates a monitoring actor for the given socket. * @param ctx the context relative to this actor. Not null. * @param socket the socket to monitor for events. Not null. */ public ZMonitor(ZContext ctx, Socket socket) { Objects.requireNonNull(ctx, "ZMonitor works only with a supplied context"); Objects.requireNonNull(socket, "Socket has to be supplied"); final MonitorActor actor = new MonitorActor(socket); final ZActor zactor = new ZActor(ctx, actor, UUID.randomUUID().toString()); agent = zactor.agent(); exit = zactor.exit(); // wait for the start of the actor agent.recv().destroy(); } private static class MonitorActor extends ZActor.SimpleActor { private static final String ERROR = "ERROR"; private static final String OK = "OK"; private final Socket monitored; // the monitored socket private final String address; // the address where events will be collected private Socket monitor; // the monitoring socket private int events; // the events to monitor private boolean verbose; public MonitorActor(ZMQ.Socket socket) { assert (socket != null); this.monitored = socket; this.address = String.format("inproc://zmonitor-%s-%s", socket.hashCode(), UUID.randomUUID()); } @Override public String premiere(Socket pipe) { return "ZMonitor-" + monitored.toString(); } @Override public List createSockets(ZContext ctx, Object... args) { monitor = ctx.createSocket(SocketType.PAIR); assert (monitor != null); return Collections.singletonList(monitor); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { pipe.send("STARTED"); } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int evts) { final zmq.ZMQ.Event event = zmq.ZMQ.Event.read(socket.base()); assert (event != null); final int code = event.event; final String address = event.addr; assert (address != null); final Event type = Event.findByCode(code); assert (type != null); final ZMsg msg = new ZMsg(); final ZFrame frame = new ZFrame(new byte[1 + 4 + address.length() + 4]); final ZNeedle needle = new ZNeedle(frame); needle.putNumber1(type.ordinal()); needle.putNumber4(code); needle.putString(address); msg.add(frame); final Object value = event.arg; if (value != null) { msg.add(value.toString()); } return msg.send(pipe, true); } @Override public boolean backstage(ZMQ.Socket pipe, ZPoller poller, int evts) { final String command = pipe.recvStr(); if (command == null) { System.out.printf("ZMonitor: Closing monitor %s : No command%n", monitored); return false; } switch (command) { case VERBOSE: verbose = Boolean.parseBoolean(pipe.recvStr()); return pipe.send(OK); case ADD_EVENTS: return addEvents(pipe); case REMOVE_EVENTS: return removeEvents(pipe); case START: return start(poller, pipe); case CLOSE: return close(poller, pipe); default: System.out.printf("ZMonitor: Closing monitor %s : Unknown command %s%n", monitored, command); pipe.send(ERROR); return false; } } private boolean addEvents(Socket pipe) { final ZMsg msg = ZMsg.recvMsg(pipe); if (msg == null) { return false; // interrupted } for (ZFrame frame : msg) { final String evt = frame.getString(ZMQ.CHARSET); final Event event = Event.valueOf(evt); if (verbose) { System.out.printf("ZMonitor: Adding" + " event %s%n", event); } events |= event.code; } return pipe.send(OK); } private boolean removeEvents(Socket pipe) { final ZMsg msg = ZMsg.recvMsg(pipe); if (msg == null) { return false; // interrupted } for (ZFrame frame : msg) { final String evt = frame.getString(ZMQ.CHARSET); final Event event = Event.valueOf(evt); if (verbose) { System.out.printf("ZMonitor: Removing" + " event %s%n", event); } events &= ~event.code; } return pipe.send(OK); } private boolean start(ZPoller poller, Socket pipe) { boolean rc = true; String err = ""; if (rc) { rc = monitored.monitor(address, events); err = "Unable to monitor socket " + monitored; } if (rc) { err = "Unable to connect monitoring socket " + monitor; rc = monitor.connect(address); } if (rc) { err = "Unable to poll monitoring socket " + monitor; rc = poller.register(monitor, ZPoller.IN); } log("tart", rc, err); if (rc) { return pipe.send(OK); } pipe.send(ERROR); return false; } private boolean close(ZPoller poller, Socket pipe) { boolean rc = poller.unregister(monitor); String err = "Unable to unregister monitoring socket " + monitor; if (rc) { err = "Unable to stop monitor socket " + monitored; rc = monitored.monitor(null, events); } log("top", rc, err); if (verbose) { System.out.printf("ZMonitor: Closing monitor %s%n", monitored); } pipe.send(rc ? OK : ERROR); return false; } private void log(String action, boolean rc, String err) { if (verbose) { if (rc) { System.out.printf("ZMonitor: S%s monitor for events %s on %s%n", action, events, monitored); } else { System.out.printf( "ZMonitor: Unable to s%s monitor for events %s (%s) on %s%n", action, events, err, monitored); } } } } } jeromq-0.6.0/src/main/java/org/zeromq/ZMsg.java000066400000000000000000000430131455771126300213110ustar00rootroot00000000000000package org.zeromq; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.Objects; import java.util.stream.Collectors; import org.zeromq.ZMQ.Socket; import zmq.util.Draft; import zmq.util.function.Consumer; /** * The ZMsg class provides methods to send and receive multipart messages * across 0MQ sockets. This class provides a list-like container interface, * with methods to work with the overall container. ZMsg messages are * composed of zero or more ZFrame objects. * *
 * // Send a simple single-frame string message on a ZMQSocket "output" socket object
 * ZMsg.newStringMsg("Hello").send(output);
 *
 * // Add several frames into one message
 * ZMsg msg = new ZMsg();
 * for (int i = 0 ; i < 10 ; i++) {
 *     msg.addString("Frame" + i);
 * }
 * msg.send(output);
 *
 * // Receive message from ZMQSocket "input" socket object and iterate over frames
 * ZMsg receivedMessage = ZMsg.recvMsg(input);
 * for (ZFrame f : receivedMessage) {
 *     // Do something with frame f (of type ZFrame)
 * }
 * 
* * Based on zmsg.c in czmq * */ public class ZMsg implements Iterable, Deque { /** * Hold internal list of ZFrame objects */ private final ArrayDeque frames = new ArrayDeque<>(); /** * Class Constructor */ public ZMsg() { } /** * Destructor. * Explicitly destroys all ZFrames contains in the ZMsg */ public void destroy() { for (ZFrame f : frames) { f.destroy(); } frames.clear(); } /** * @return total number of bytes contained in all ZFrames in this ZMsg */ public long contentSize() { long size = 0; for (ZFrame f : frames) { size += f.size(); } return size; } /** * Add a String as a new ZFrame to the end of list * @param str * String to add to list */ public ZMsg addString(String str) { frames.add(new ZFrame(str)); return this; } /** * Creates copy of this ZMsg. * Also duplicates all frame content. * @return * The duplicated ZMsg object, else null if this ZMsg contains an empty frame set */ public ZMsg duplicate() { if (frames.isEmpty()) { return null; } else { ZMsg msg = new ZMsg(); for (ZFrame f : frames) { msg.add(f.duplicate()); } return msg; } } /** * Push frame plus empty frame to front of message, before 1st frame. * Message takes ownership of frame, will destroy it when message is sent. * @param frame */ public ZMsg wrap(ZFrame frame) { if (frame != null) { push(new ZFrame("")); push(frame); } return this; } /** * Pop frame off front of message, caller now owns frame. * If next frame is empty, pops and destroys that empty frame * (e.g. useful when unwrapping ROUTER socket envelopes) * @return * Unwrapped frame */ public ZFrame unwrap() { if (size() == 0) { return null; } ZFrame f = pop(); ZFrame empty = getFirst(); if (empty.hasData() && empty.size() == 0) { empty = pop(); empty.destroy(); } return f; } /** * Send message to 0MQ socket. * * @param socket * 0MQ socket to send ZMsg on. * @return true if send is success, false otherwise */ public boolean send(Socket socket) { return send(socket, true); } /** * Send message to 0MQ socket, destroys contents after sending if destroy param is set to true. * If the message has no frames, sends nothing but still destroy()s the ZMsg object * @param socket * 0MQ socket to send ZMsg on. * @return true if send is success, false otherwise */ public boolean send(Socket socket, boolean destroy) { if (socket == null) { throw new IllegalArgumentException("socket is null"); } if (frames.isEmpty()) { return true; } boolean ret = true; Iterator i = frames.iterator(); while (i.hasNext()) { ZFrame f = i.next(); ret = f.sendAndKeep(socket, (i.hasNext()) ? ZFrame.MORE : 0); if (!ret) { break; } } if (destroy) { destroy(); } return ret; } /** * Receives message from socket, returns ZMsg object or null if the * recv was interrupted. Does a blocking recv, if you want not to block then use * the ZLoop class or ZMQ.Poller to check for socket input before receiving or recvMsg with flag ZMQ.DONTWAIT. * @param socket * @return * ZMsg object, null if interrupted */ public static ZMsg recvMsg(Socket socket) { return recvMsg(socket, 0); } /** * Receives message from socket, returns ZMsg object or null if the * recv was interrupted. * @param socket * @param wait true to wait for next message, false to do a non-blocking recv. * @return * ZMsg object, null if interrupted */ public static ZMsg recvMsg(Socket socket, boolean wait) { return recvMsg(socket, wait ? 0 : ZMQ.DONTWAIT); } /** * Receives message from socket, returns ZMsg object or null if the * recv was interrupted. Setting the flag to ZMQ.DONTWAIT does a non-blocking recv. * @param socket * @param flag see ZMQ constants * @return * ZMsg object, null if interrupted */ public static ZMsg recvMsg(Socket socket, int flag) { if (socket == null) { throw new IllegalArgumentException("socket is null"); } ZMsg msg = new ZMsg(); while (true) { ZFrame f = ZFrame.recvFrame(socket, flag); if (f == null) { // If receive failed or was interrupted msg.destroy(); msg = null; break; } msg.add(f); if (!f.hasMore()) { break; } } return msg; } /** * This API is in DRAFT state and is subject to change at ANY time until declared stable * handle incoming message with a handler * * @param socket * @param flags see ZMQ constants * @param handler handler to handle incoming message * @param exceptionHandler handler to handle exceptions */ @Draft public static void recvMsg(ZMQ.Socket socket, int flags, Consumer handler, Consumer exceptionHandler) { try { handler.accept(ZMsg.recvMsg(socket, flags)); } catch (ZMQException e) { exceptionHandler.accept(e); } } /** * This API is in DRAFT state and is subject to change at ANY time until declared stable * handle incoming message with a handler * * @param socket * @param flags see ZMQ constants * @param handler handler to handle incoming message */ @Draft public static void recvMsg(ZMQ.Socket socket, int flags, Consumer handler) { handler.accept(ZMsg.recvMsg(socket, flags)); } /** * Save message to an open data output stream. *

* Data saved as: * 4 bytes: number of frames * For every frame: * 4 bytes: byte size of frame data * + n bytes: frame byte data * * @param msg * ZMsg to save * @param file * DataOutputStream * @return * True if saved OK, else false */ public static boolean save(ZMsg msg, DataOutputStream file) { if (msg == null) { return false; } try { // Write number of frames file.writeInt(msg.size()); if (msg.size() > 0) { for (ZFrame f : msg) { // Write byte size of frame file.writeInt(f.size()); // Write frame byte data file.write(f.getData()); } } return true; } catch (IOException e) { return false; } } /** * Load / append a ZMsg from an open DataInputStream * * @param file * DataInputStream connected to file * @return * ZMsg object */ public static ZMsg load(DataInputStream file) { if (file == null) { return null; } ZMsg rcvMsg = new ZMsg(); try { int msgSize = file.readInt(); if (msgSize > 0) { int msgNbr = 0; while (++msgNbr <= msgSize) { int frameSize = file.readInt(); byte[] data = new byte[frameSize]; file.read(data); rcvMsg.add(new ZFrame(data)); } } return rcvMsg; } catch (IOException e) { return null; } } /** * Create a new ZMsg from one or more Strings * * @param strings * Strings to add as frames. * @return * ZMsg object */ public static ZMsg newStringMsg(String... strings) { ZMsg msg = new ZMsg(); for (String data : strings) { msg.addString(data); } return msg; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ZMsg zMsg = (ZMsg) o; //based on AbstractList Iterator e1 = frames.iterator(); Iterator e2 = zMsg.frames.iterator(); while (e1.hasNext() && e2.hasNext()) { ZFrame o1 = e1.next(); ZFrame o2 = e2.next(); if (!(Objects.equals(o1, o2))) { return false; } } return !(e1.hasNext() || e2.hasNext()); } @Override public int hashCode() { if (frames.isEmpty()) { return 0; } int result = 1; for (ZFrame frame : frames) { result = 31 * result + (frame == null ? 0 : frame.hashCode()); } return result; } /** * Dump the message in human readable format. This should only be used * for debugging and tracing, inefficient in handling large messages. **/ public ZMsg dump(Appendable out) { try { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); pw.printf("--------------------------------------\n"); for (ZFrame frame : frames) { pw.printf("[%03d] %s\n", frame.size(), frame); } out.append(sw.getBuffer()); sw.close(); } catch (IOException e) { throw new RuntimeException("Message dump exception " + super.toString(), e); } return this; } public ZMsg dump() { dump(System.out); return this; } // ********* Convenience Deque methods for common data types *** // public ZMsg addFirst(String stringValue) { addFirst(new ZFrame(stringValue)); return this; } public ZMsg addFirst(byte[] data) { addFirst(new ZFrame(data)); return this; } public ZMsg addLast(String stringValue) { addLast(new ZFrame(stringValue)); return this; } public ZMsg addLast(byte[] data) { addLast(new ZFrame(data)); return this; } // ********* Convenience Queue methods for common data types *** // public ZMsg push(String str) { push(new ZFrame(str)); return this; } public ZMsg push(byte[] data) { push(new ZFrame(data)); return this; } public boolean add(String stringValue) { return add(new ZFrame(stringValue)); } public boolean add(byte[] data) { return add(new ZFrame(data)); } /** * Adds a string as a new frame in the message. * * @param stringValue the value to add * @return this */ public ZMsg append(String stringValue) { add(stringValue); return this; } /** * Adds bytes as a new frame in the message. * * @param data the value to add * @return this */ public ZMsg append(byte[] data) { add(data); return this; } // ********* Implement Iterable Interface *************** // @Override public Iterator iterator() { return frames.iterator(); } // ********* Implement Deque Interface ****************** // @Override public boolean addAll(Collection arg0) { return frames.addAll(arg0); } @Override public void clear() { frames.clear(); } @Override public boolean containsAll(Collection arg0) { return frames.containsAll(arg0); } @Override public boolean isEmpty() { return frames.isEmpty(); } @Override public boolean removeAll(Collection arg0) { return frames.removeAll(arg0); } @Override public boolean retainAll(Collection arg0) { return frames.retainAll(arg0); } @Override public Object[] toArray() { return frames.toArray(); } @Override public T[] toArray(T[] arg0) { return frames.toArray(arg0); } @Override public boolean add(ZFrame e) { return frames.add(e); } @Override public void addFirst(ZFrame e) { frames.addFirst(e); } @Override public void addLast(ZFrame e) { frames.addLast(e); } @Override public boolean contains(Object o) { return frames.contains(o); } @Override public Iterator descendingIterator() { return frames.descendingIterator(); } @Override public ZFrame element() { return frames.element(); } @Override public ZFrame getFirst() { return frames.peekFirst(); } @Override public ZFrame getLast() { return frames.peekLast(); } @Override public boolean offer(ZFrame e) { return frames.offer(e); } @Override public boolean offerFirst(ZFrame e) { return frames.offerFirst(e); } @Override public boolean offerLast(ZFrame e) { return frames.offerLast(e); } @Override public ZFrame peek() { return frames.peek(); } @Override public ZFrame peekFirst() { return frames.peekFirst(); } @Override public ZFrame peekLast() { return frames.peekLast(); } @Override public ZFrame poll() { return frames.poll(); } @Override public ZFrame pollFirst() { return frames.pollFirst(); } @Override public ZFrame pollLast() { return frames.pollLast(); } @Override public ZFrame pop() { return frames.poll(); } /** * Pop a ZFrame and return the toString() representation of it. * * @return toString version of pop'ed frame, or null if no frame exists. */ public String popString() { ZFrame frame = pop(); if (frame == null) { return null; } return frame.toString(); } @Override public void push(ZFrame e) { frames.push(e); } @Override public ZFrame remove() { return frames.remove(); } @Override public boolean remove(Object o) { return frames.remove(o); } @Override public ZFrame removeFirst() { return frames.pollFirst(); } @Override public boolean removeFirstOccurrence(Object o) { return frames.removeFirstOccurrence(o); } @Override public ZFrame removeLast() { return frames.pollLast(); } @Override public boolean removeLastOccurrence(Object o) { return frames.removeLastOccurrence(o); } @Override public int size() { return frames.size(); } public ZMsg append(ZMsg msg) { if (msg == null) { return this; } // Tests explicitly check appending a ZMsg to itself, protect that if (msg != this) { frames.addAll(msg.frames); } else { frames.addAll(msg.frames.clone()); } return this; } /** * Returns pretty string representation of multipart message: * [ frame0, frame1, ..., frameN ] * * @return toString version of ZMsg object */ @Override public String toString() { String joined = frames.stream().map(ZFrame::toString).collect(Collectors.joining(", ")); return new StringBuilder("[ ").append(joined).append(" ]").toString(); } } jeromq-0.6.0/src/main/java/org/zeromq/ZPoller.java000066400000000000000000000776051455771126300220360ustar00rootroot00000000000000package org.zeromq; import java.io.Closeable; import java.nio.channels.SelectableChannel; import java.nio.channels.Selector; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import zmq.poll.PollItem; import zmq.util.Objects; import zmq.util.function.BiFunction; import zmq.util.function.Function; /** * Rewritten poller for ØMQ. *
* Polls selectable channels and sockets for specified events. * *

This poller can be used in two ways:

* *
    *
  • the traditional one, where you make something like *
     * {@code
                ZPoller poller = ...
                poller.register(socket, ZPoller.POLLIN);
                poller.register(channel, ZPoller.OUT);
    
                int events = poller.poll(-1L);
    
                if (poller.isReadable(socket)) {
                    ...
                }
                if (poller.writable(channel)) {
                    ...
                }
    }
    
    *
  • the event-driven way *
     * {@code
     *
                ZPoller poller = ...
    
                poller.setGlobalHandler(...)
                ZPoller.EventsHandler handler = ...
    
                // The events method of the handler will be called
                poller.register(channel, handler, ZPoller.IN);
    
                // The events method of the global handler will be called
                poller.register(socket, ZPoller.POLLOUT);
    
                poller.poll(-1L);
                // handlers have been called
    }
    
* The motivations of this rewriting are: *
    *
  • the bare poller used {@link zmq.ZMQ#poll(Selector, PollItem[], int, long)}. This method did not allow * to choose the selector used for polling, relying on a ThreadLocal, which is dangerous. *
  • the bare poller use algorithms tailored for languages with manual allocation. * No need here as Java allows more flexibility. TODO There still may be a small penalty cost. *
* *

It’s thread safe, but newly registered items will only be handled at the next poll iteration, * so if {@code poller.poll(-1L)} is used, adding items will have no effect. The situation is the same with the global handler

*/ // Poller for 0MQ. public class ZPoller implements Closeable { // contract for events public interface EventsHandler { /** * Called when the poller intercepts events. * * @param socket the socket with events * @param events the interesting events as an ORed combination of IN, OUT, ERR * @return true to continue the polling, false to stop it */ boolean events(Socket socket, int events); /** * Called when the poller intercepts events. * * @param channel the channel with events * @param events the interesting events as an ORed combination of IN, OUT, ERR * @return true to continue the polling, false to stop it */ boolean events(SelectableChannel channel, int events); } public static class ComposeEventsHandler implements EventsHandler { private final BiFunction sockets; private final BiFunction channels; public ComposeEventsHandler(BiFunction sockets, BiFunction channels) { super(); this.sockets = sockets; this.channels = channels; } @Override public boolean events(Socket socket, int events) { assert (sockets != null) : "A Socket Handler needs to be set"; return sockets.apply(socket, events); } @Override public boolean events(SelectableChannel channel, int events) { assert (channels != null) : "A SelectableChannel Handler needs to be set"; return channels.apply(channel, events); } } // contract for items. Useful for providing own implementation. public interface ItemHolder { // the inner ZMQ poll item PollItem item(); // the related ZeroMQ socket Socket socket(); // the associated events handler EventsHandler handler(); } // contract for items creation. Useful for delegating. public interface ItemCreator { /** * Creates a new holder for a poll item. * * @param socket the socket to poll * @param handler the optional handler for polled events * @param events the interested events * @return a new poll item holder */ ItemHolder create(Socket socket, EventsHandler handler, int events); /** * Creates a new holder for a poll item. * * @param channel the channel to poll * @param handler the optional handler for polled events. * @param events the interested events * @return a new poll item holder */ ItemHolder create(SelectableChannel channel, EventsHandler handler, int events); } // re-re-implementation of poll item public static class ZPollItem extends ZMQ.PollItem implements ItemHolder { private final EventsHandler handler; public ZPollItem(final Socket socket, final EventsHandler handler, final int ops) { super(socket, ops); this.handler = handler; assert (item() != null); } public ZPollItem(final SelectableChannel channel, final EventsHandler handler, final int ops) { super(channel, ops); this.handler = handler; assert (item() != null); } @Override public PollItem item() { return base(); } @Override public Socket socket() { return getSocket(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((item() == null) ? 0 : item().hashCode()); result = prime * result + ((getRawSocket() == null) ? 0 : getRawSocket().hashCode()); result = prime * result + ((socket() == null) ? 0 : socket().hashCode()); result = prime * result + ((handler() == null) ? 0 : handler().hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof ItemHolder)) { return false; } ItemHolder other = (ItemHolder) obj; // this.item() is never null if (other.item() == null) { return false; } // no checking on item() as zmq.PollItem does not override equals() if (item().getRawSocket() == null) { if (other.item().getRawSocket() != null) { return false; } } else if (!item().getRawSocket().equals(other.item().getRawSocket())) { return false; } if (socket() == null) { if (other.socket() != null) { return false; } } else if (!socket().equals(other.socket())) { return false; } if (handler() == null) { return other.handler() == null; } else { return handler().equals(other.handler()); } } @Override public EventsHandler handler() { return handler; } } private class CompositePollItem implements ItemHolder, EventsHandler { private final Set holders = ConcurrentHashMap.newKeySet(); private final Socket socket; private final SelectableChannel channel; private PollItem item; public CompositePollItem(final Object socketOrChannel) { this.socket = socketOrChannel instanceof Socket ? (Socket) socketOrChannel : null; this.channel = socketOrChannel instanceof SelectableChannel ? (SelectableChannel) socketOrChannel : null; assert (socket != null || channel != null); } @Override public PollItem item() { if (item == null) { item = createItem(); } return item; } private PollItem createItem() { if (socket == null) { return new PollItem(channel, ops()); } else { return new PollItem(socket.base(), ops()); } } private int ops() { int ops = 0; for (ItemHolder holder : holders) { int interest = holder.item().zinterestOps(); ops |= interest; } return ops; } @Override public Socket socket() { return socket; } @Override public EventsHandler handler() { return this; } @Override public boolean events(Socket socket, int events) { return doEvent(h -> h.events(socket, events), events); } @Override public boolean events(SelectableChannel channel, int events) { return doEvent(h -> h.events(channel, events), events); } private boolean doEvent(Function handle, int events) { boolean has = false; boolean first = true; for (ItemHolder holder : holders) { if (holder.item().hasEvent(events)) { if (first) { first = false; has = true; } EventsHandler handler = holder.handler() == null ? currentGlobalHandler.get() : holder.handler(); if (handler != null) { has &= handle.apply(handler); } } } return has; } } /******************************************************************************/ /* 0MQ socket events */ /******************************************************************************/ /* * These values can be ORed to specify what we want to poll for. */ public static final int POLLIN = Poller.POLLIN; public static final int POLLOUT = Poller.POLLOUT; public static final int POLLERR = Poller.POLLERR; // same values with shorter writing public static final int IN = POLLIN; public static final int OUT = POLLOUT; public static final int ERR = POLLERR; // same values with semantic consistency public static final int READABLE = POLLIN; public static final int WRITABLE = POLLOUT; /** * Creates a new poller based on the current one. * This will be a shadow poller, sharing the same selector and items creator. * The global events handler will not be shared. * * @param poller the main poller. */ public ZPoller(final ZPoller poller) { this(poller.creator, poller.selector); } /** * Creates a new poller with a given selector for operational polling. * * @param selector the selector to use for polling. */ public ZPoller(final Selector selector) { this(new SimpleCreator(), selector); } /** * Creates a new poller attached to a given context that will provide * selector for operational polling. * * @param context * the context that will provide the selector to use for polling. */ public ZPoller(final ZContext context) { this(new SimpleCreator(), context); } /** * Creates a new poller based on the current one. This will be a shadow * poller, sharing the same selector. The global events handler will not be * shared. * * @param creator the items creator * @param poller the main poller. */ public ZPoller(final ItemCreator creator, final ZPoller poller) { this(creator, poller.selector); } /** * Creates a new poller attached to a given context that will provide * selector for operational polling. * * @param creator * the items creator * @param context * the context that will provide the selector to use for polling. */ public ZPoller(final ItemCreator creator, final ZContext context) { this(creator, context.selector()); } /** * Creates a new poller. * * @param creator the items creator * @param selector the selector to use for polling. */ private ZPoller(final ItemCreator creator, final Selector selector) { Objects.requireNonNull(creator, "Item creator is mandatory for ZPoller"); Objects.requireNonNull(selector, "Selector is mandatory for ZPoller"); this.creator = creator; this.selector = selector; items = new ConcurrentHashMap<>(); } // creates a new poll item protected ItemHolder create(final Socket socket, final EventsHandler handler, final int events) { Objects.requireNonNull(socket, "Socket has to be non-null"); return creator.create(socket, handler, events); } // creates a new poll item protected ItemHolder create(final SelectableChannel channel, final EventsHandler handler, final int events) { Objects.requireNonNull(channel, "Channel has to be non-null"); return creator.create(channel, handler, events); } /** * Sets the global events handler for all registered sockets. * * @param globalHandler the events handler to set */ public void setGlobalHandler(final EventsHandler globalHandler) { this.globalHandler.set(globalHandler); } /** * Returns the global events handler for all registered sockets. * * @return the global events handler for all registered sockets. */ public EventsHandler getGlobalHandler() { return globalHandler.get(); } /** * Register a Socket for polling on specified events. * * @param socket the registering socket. * @param handler the events handler for this socket * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final Socket socket, final BiFunction handler, final int events) { return register(socket, new ComposeEventsHandler(handler, null), events); } /** * Register a Socket for polling on specified events. * * @param socket the registering socket. * @param handler the events handler for this socket * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final Socket socket, final EventsHandler handler, final int events) { if (socket == null) { return false; } return add(socket, create(socket, handler, events)); } /** * Register a Socket for polling on all events. * * @param socket the registering socket. * @param handler the events handler for this socket * @return true if registered, otherwise false */ public final boolean register(final Socket socket, final EventsHandler handler) { return register(socket, handler, POLLIN | POLLOUT | POLLERR); } /** * Register a Socket for polling on specified events, using the global {@link EventsHandler}. * * @param socket the registering socket. * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final Socket socket, final int events) { return register(socket, globalHandler.get(), events); } /** * Registers a SelectableChannel for polling on specified events. * * @param channel the registering channel. * @param handler the events handler for this channel * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final SelectableChannel channel, final BiFunction handler, final int events) { return register(channel, new ComposeEventsHandler(null, handler), events); } /** * Registers a SelectableChannel for polling on specified events. * * @param channel the registering channel. * @param handler the events handler for this channel * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final SelectableChannel channel, final EventsHandler handler, final int events) { if (channel == null) { return false; } return add(channel, create(channel, handler, events)); } /** * Registers a SelectableChannel for polling on all events. * * @param channel the registering channel. * @param handler the events handler for this channel * @return true if registered, otherwise false */ public final boolean register(final SelectableChannel channel, final EventsHandler handler) { return register(channel, handler, IN | OUT | ERR); } /** * Registers a SelectableChannel for polling on specified events. * * @param channel the registering channel. * @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR. * @return true if registered, otherwise false */ public final boolean register(final SelectableChannel channel, final int events) { return register(channel, globalHandler.get(), events); } /** * * @return the number of registered object, mixing Sockets and SelectableChannels */ public int registered() { return items.size(); } /** * Register an ItemHolder for polling on specified events. * * @param item the registering item. * @return true if registered, otherwise false */ public final boolean register(final ItemHolder item) { return add(null, item); } /** * Unregister a Socket or SelectableChannel for polling on the specified events. * * @param socketOrChannel the Socket or SelectableChannel to be unregistered * @return true if unregistered, otherwise false * TODO would it be useful to unregister only for specific events ? */ public final synchronized boolean unregister(final Object socketOrChannel) { if (socketOrChannel == null) { return false; } CompositePollItem removedItems = items.remove(socketOrChannel); return removedItems != null; } /******************************************************************************/ /* POLLING | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |*/ /******************************************************************************/ /** * Issue a poll call, using the specified timeout value. *

* Since ZeroMQ 3.0, the timeout parameter is in milliseconds, * but prior to this the unit was microseconds. *

If timeout is -1 and there is no object registered, it will return immediately

* * @param timeout * the timeout, as per zmq_poll (); * if -1, it will block indefinitely until an event * happens; if 0, it will return immediately; * otherwise, it will wait for at most that many * milliseconds/microseconds (see above). * @see "http://api.zeromq.org/3-0:zmq-poll" * @return how many objects where signaled by poll () */ public int poll(final long timeout) { return poll(timeout, true); } /** * Issue a poll call, using the specified timeout value. *

If timeout is -1 and there is no object registered, it will return immediately

* * @param timeout the timeout, as per zmq_poll (); * @param dispatchEvents true to dispatch events using items handler and the global one. * @see "http://api.zeromq.org/3-0:zmq-poll" * @return how many objects where signaled by poll () */ protected int poll(final long timeout, final boolean dispatchEvents) { // Local copy of the items, for consistency Set allPolled = new HashSet<>(items.values()); // get all the raw items final Set pollItems = allPolled.stream() .map(CompositePollItem::item) .collect(Collectors.toSet()); // polling time final int rc = poll(selector, timeout, pollItems); if (!dispatchEvents) { // raw result return rc; } if (dispatch(allPolled)) { // returns event counts after dispatch if everything went fine return rc; } // error in dispatching return -1; } // does the effective polling protected int poll(final Selector selector, final long tout, final Collection items) { return zmq.ZMQ.poll(selector, items.toArray(new PollItem[0]), items.size(), tout); } private boolean dispatch(Set allPolled) { return subDispatch(allPolled); } /** * Dispatches the polled events. * * @param all the items used for dispatching * @param size Unused * @return true if correctly dispatched, false in case of error * @deprecated Uses {@link #dispatch(Collection)} instead, as size is unused */ @SuppressWarnings("unused") @Deprecated protected boolean dispatch(final Collection all, int size) { return subDispatch(all); } /** * Dispatches the polled events. * * @param allDispatched the items used for dispatching * @return true if correctly dispatched, false in case of error */ protected boolean dispatch(final Collection allDispatched) { return subDispatch(allDispatched); } /** * dispatches all the polled events to their respective handlers * * @return true if correctly dispatched, false in case of error */ public boolean dispatch() { return subDispatch(new HashSet<>(items.values())); } private boolean subDispatch(final Collection allDispatched) { EventsHandler localGlobalHandler = currentGlobalHandler.get(); for (ItemHolder holder : allDispatched) { EventsHandler handler = holder.handler(); if (handler == null) { handler = localGlobalHandler; } if (handler == null) { // no handler, short-circuit continue; } final PollItem item = holder.item(); final int events = item.readyOps(); if (events <= 0) { // no events, short-circuit continue; } final Socket socket = holder.socket(); final SelectableChannel channel = holder.item().getRawSocket(); if (socket != null) { assert (channel == null); // dispatch on socket if (!handler.events(socket, events)) { return false; } } // dispatch on channel if (channel != null && !handler.events(channel, events)) { return false; } } // Release the global handler, so it can be refreshed currentGlobalHandler.remove(); return true; } /******************************************************************************/ /*| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | POLLING */ /******************************************************************************/ /** * Tells if a channel is readable from this poller. * * @param channel the channel to ask for. * @return true if readable, otherwise false */ public boolean isReadable(final SelectableChannel channel) { return readable((Object) channel); } public boolean readable(final SelectableChannel channel) { return readable((Object) channel); } /** * Tells if a socket is readable from this poller. * * @param socket the socket to ask for. * @return true if readable, otherwise false */ public boolean isReadable(final Socket socket) { return readable(socket); } public boolean readable(final Socket socket) { return readable((Object) socket); } // checks for read event public boolean readable(final Object socketOrChannel) { final PollItem it = filter(socketOrChannel, READABLE); if (it == null) { return false; } return it.isReadable(); } public boolean pollin(final Socket socket) { return isReadable(socket); } public boolean pollin(final SelectableChannel channel) { return isReadable(channel); } /** * Tells if a channel is writable from this poller. * * @param channel the channel to ask for. * @return true if writable, otherwise false */ public boolean isWritable(final SelectableChannel channel) { return writable((Object) channel); } public boolean writable(final SelectableChannel channel) { return writable((Object) channel); } /** * Tells if a socket is writable from this poller. * * @param socket the socket to ask for. * @return true if writable, otherwise false */ public boolean isWritable(final Socket socket) { return writable((Object) socket); } public boolean writable(final Socket socket) { return writable((Object) socket); } // checks for write event public boolean writable(final Object socketOrChannel) { final PollItem it = filter(socketOrChannel, WRITABLE); if (it == null) { return false; } return it.isWritable(); } public boolean pollout(final Socket socket) { return isWritable(socket); } public boolean pollout(final SelectableChannel channel) { return isWritable(channel); } /** * Tells if a channel is in error from this poller. * * @param channel the channel to ask for. * @return true if in error, otherwise false */ public boolean isError(final SelectableChannel channel) { return error((Object) channel); } public boolean error(final SelectableChannel channel) { return error((Object) channel); } /** * Tells if a socket is in error from this poller. * * @param socket the socket to ask for. * @return true if in error, otherwise false */ public boolean isError(final Socket socket) { return error((Object) socket); } public boolean error(final Socket socket) { return error((Object) socket); } // checks for error event public boolean error(final Object socketOrChannel) { final PollItem it = filter(socketOrChannel, ERR); if (it == null) { return false; } return it.isError(); } public boolean pollerr(final Socket socket) { return isError(socket); } public boolean pollerr(final SelectableChannel channel) { return isError(channel); } /** * Destroys the poller. Does actually nothing. */ @Override public void close() { destroy(); } /** * Destroys the poller without exception. */ public void destroy() { // Nothing to do } // selector used for polling private final Selector selector; // creator of items private final ItemCreator creator; // managed items private final Map items; // TODO set of handlers, each with its specified events? // optional global events handler private final AtomicReference globalHandler = new AtomicReference<>(null); // Use to cache the global handler for the current thread private final ThreadLocal currentGlobalHandler = ThreadLocal.withInitial(globalHandler::get); // simple creator for poll items public static class SimpleCreator implements ItemCreator { @Override public ItemHolder create(final Socket socket, final EventsHandler handler, final int events) { return new ZPollItem(socket, handler, events); } @Override public ItemHolder create(final SelectableChannel channel, final EventsHandler handler, final int events) { return new ZPollItem(channel, handler, events); } } // add an item to this poller protected boolean add(final Object socketOrChannel, final ItemHolder holder) { Object key = Optional.ofNullable(socketOrChannel).orElseGet(() -> computeSocketOrChannel(holder)); CompositePollItem aggregate = items.computeIfAbsent(key, CompositePollItem::new); return aggregate.holders.add(holder); } private Object computeSocketOrChannel(final ItemHolder holder) { Socket socket = holder.socket(); SelectableChannel ch = holder.item().getRawSocket(); if (ch == null && socket == null) { throw new IllegalArgumentException("Needs a socket or a channel to register"); } else if (socket != null) { return socket; } else { return ch; } } /** * Return a new HashSet * @param size the initial capacity of the hash table * @return a new hash set * @deprecated useless */ @Deprecated protected Set createContainer(int size) { return new HashSet<>(size); } /** * Return a copy of all the items of this poller. */ protected Collection items() { return new HashSet<>(items.values()); } // gets all the items of this poller regarding the given input protected Iterable items(final Object socketOrChannel) { if (socketOrChannel == null || ! items.containsKey(socketOrChannel)) { return Collections.emptySet(); } else { return items.get(socketOrChannel).holders; } } // filters items to get the first one matching the criteria, or null if none found protected PollItem filter(final Object socketOrChannel, final int events) { return Optional.ofNullable(socketOrChannel) .map(items::get) .map(CompositePollItem::item) .filter(pi -> pi.hasEvent(events)) .orElse(null); } } jeromq-0.6.0/src/main/java/org/zeromq/ZProxy.java000066400000000000000000001253111455771126300217060ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.zeromq.ZActor.Actor; import org.zeromq.ZAgent.SelectorCreator; import org.zeromq.ZMQ.Socket; import org.zeromq.ZStar.Exit; import zmq.Msg; import zmq.SocketBase; /** * Implementation of a remotely controlled proxy for 0MQ, using {@link ZActor}. *
* The goals of this implementation are to delegate the creation of sockets * in a background thread via a callback interface to ensure their correct use * and to provide ultimately to end-users the following features. * *

Basic features:

*
    *
  • Remote Control *
      *
    • Start: if was paused, flushes the pending messages *
    • Pause: lets the socket queues accumulate messages according to their types *
    • Stop: Shutdowns the proxy, can be restarted *
    • Status: Retrieves the status of the proxy *
    • Cold Restart: Closes and recreates the connections *
    • {@link #restart(ZMsg) Hot Restart}: User-defined behavior with custom messages *
    • {@link #configure(ZMsg) Configure}: User-defined behavior with custom messages *
    • {@link #command(String, boolean)} ...: Custom commands of your own *
    • Exit: Definitive shutdown of the proxy and its control *
    * All the non-custom commands can be performed in asynchronous or synchronous mode. *
    *
  • Proxy mechanism ensured by pluggable pumps *
      *
    • with built-in low-level {@link org.zeromq.ZProxy.ZmqPump} (zmq.ZMQ): useful for performances *
    • with built-in high-level {@link org.zeromq.ZProxy.ZPump} (ZeroMQ): useful for {@link org.zeromq.ZProxy.ZPump.Transformer message transformation}, lower performances *
    • with your own-custom proxy pump implementing a {@link Pump 1-method interface} *
    *

*
* You can have all the above non-customizable features in about these lines of code: *

 * {@code
        final ZProxy.Proxy provider = new ZProxy.SimpleProxy()
        {
            public Socket create(ZContext ctx, ZProxy.Plug place, Object ... args)
            {
                assert ("TEST".equals(args[0]);
                Socket socket = null;
                if (place == ZProxy.Plug.FRONT) {
                    socket = ctx.createSocket(ZMQ.ROUTER);
                }
                if (place == ZProxy.Plug.BACK) {
                    socket = ctx.createSocket(ZMQ.DEALER);
                }
                return socket;
            }

            public void configure(Socket socket, ZProxy.Plug place, Object ... args)
            {
                assert ("TEST".equals(args[0]);
                int port = -1;
                if (place == ZProxy.Plug.FRONT) {
                    port = socket.bind("tcp://127.0.0.1:6660");
                }
                if (place == ZProxy.Plug.BACK) {
                    port = socket.bind("tcp://127.0.0.1:6661");
                }
                if (place == ZProxy.Plug.CAPTURE && socket != null) {
                    socket.bind("tcp://127.0.0.1:4263");
                }
            }
        };

        ZProxy proxy = ZProxy.newProxy("ProxyOne", provider, "ABRACADABRA", Arrays.asList("TEST"));
}
 * 
* Once created, the proxy is not started. You have to perform first a start command on it. * This choice was made because it is easier for a user to start it with one line of code than for the code to internally handle * different possible starting states (after all, someone may want the proxy started but paused at first or configured in a specific way?) * and because the a/sync stuff was funnier. Life is unfair ... * Or maybe an idea is floating in the air? *
* You can then use it like this: *
 * {@code
        final boolean async = false, sync = true;
        String status = null;
        status = proxy.status();
        status = proxy.pause(sync);
        status = proxy.start(async);
        status = proxy.restart(new ZMsg());
        status = proxy.status(async);
        status = proxy.stop(sync);
        boolean here = proxy.sign();
        ZMsg cfg = new ZMsg();
        msg.add("CONFIG-1");
        ZMsg rcvd = proxy.configure(cfg);
        proxy.exit();
        status = proxy.status(sync);
        assert (!proxy.started());
   }
 * 
* * A {@link #command(Command, boolean) programmatic interface} with enums is also available. * * */ // Proxy for 0MQ. public class ZProxy { /** * Possible places for sockets in the proxy. */ public enum Plug { FRONT, // The position of the frontend socket. BACK, // The position of the backend socket. CAPTURE // The position of the capture socket. } // Contract for socket creation and customizable configuration in proxy threading. public interface Proxy { /** * Creates and initializes (bind, options ...) the socket for the given plug in the proxy. * The proxy will close them afterwards, and the context as well if not provided in the constructor. * There is no need to keep a reference on the created socket or the context given in parameter. * * @param ctx the context used for initialization. * @param place the position for the future created socket in the proxy. * @param args the optional array of arguments that has been passed at the creation of the ZProxy. * @return the created socket. Possibly null only for capture. */ Socket create(ZContext ctx, Plug place, Object... args); /** * Configures the given socket. * * @param socket the socket to configure * @param place the position for the socket in the proxy * @param args the optional array of arguments that has been passed at the creation of the ZProxy. * @return true if successfully configured, otherwise false */ boolean configure(Socket socket, Plug place, Object... args) throws IOException; /** * Performs a hot restart of the given socket. * Usually an unbind/bind but you can use whatever method you like. * * @param cfg the custom configuration message sent by the control. * @param socket the socket to hot restart * @param place the position for the socket in the proxy * @param args the optional array of arguments that has been passed at the creation of the ZProxy. * @return true to perform a cold restart instead, false to do nothing. All the results will be collected from calls for all plugs. * If any of them returns true, the cold restart is performed. */ boolean restart(ZMsg cfg, Socket socket, Plug place, Object... args) throws IOException; /** * Configures the proxy with a custom message. *

* Note: you need to send one (1) mandatory custom response message with the pipe before the end of this call. * * @param pipe the control pipe * @param cfg the custom configuration message sent by the control * @param frontend the frontend socket * @param backend the backend socket * @param capture the optional capture socket * @param args the optional array of arguments that has been passed at the creation of the ZProxy. * @return true to continue the proxy, false to exit */ boolean configure(Socket pipe, ZMsg cfg, Socket frontend, Socket backend, Socket capture, Object... args); /** * Handles a custom command not recognized by the proxy. *

* Note: you need to send the current state at the end of the call. * * @param pipe the control pipe * @param cmd the unrecognized command * @param frontend the frontend socket * @param backend the backend socket * @param capture the optional capture socket * @param args the optional array of arguments that has been passed at the creation of the ZProxy. * * @return true to continue the proxy, false to exit */ boolean custom(Socket pipe, String cmd, Socket frontend, Socket backend, Socket capture, Object... args); // this may be useful abstract class SimpleProxy implements Proxy { @Override public boolean restart(ZMsg cfg, Socket socket, Plug place, Object... args) { return true; } @Override public boolean configure(Socket pipe, ZMsg cfg, Socket frontend, Socket backend, Socket capture, Object... args) { return true; } @Override public boolean custom(Socket pipe, String cmd, Socket frontend, Socket backend, Socket capture, Object... args) { return true; } } } /** * Creates a new proxy in a ZeroMQ way. * This proxy will be less efficient than the * {@link #newZProxy(ZContext, String, org.zeromq.ZProxy.Proxy, String, Object...) low-level one}. * * @param ctx the context used for the proxy. * Possibly null, in this case a new context will be created and automatically destroyed afterwards. * @param name the name of the proxy. Possibly null. * @param selector the creator of the selector used for the internal polling. Not null. * @param sockets the sockets creator of the proxy. Not null. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * * @return the created proxy. * @deprecated use {@link #newZProxy(ZContext, String, Proxy, String, Object...)} instead. */ @Deprecated public static ZProxy newZProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets, String motdelafin, Object... args) { return newZProxy(ctx, name, sockets, motdelafin, args); } /** * Creates a new proxy in a ZeroMQ way. * This proxy will be less efficient than the * {@link #newZProxy(ZContext, String, org.zeromq.ZProxy.Proxy, String, Object...) low-level one}. * * @param ctx the context used for the proxy. * Possibly null, in this case a new context will be created and automatically destroyed afterwards. * @param name the name of the proxy. Possibly null. * @param sockets the sockets creator of the proxy. Not null. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * * @return the created proxy. */ public static ZProxy newZProxy(ZContext ctx, String name, Proxy sockets, String motdelafin, Object... args) { return new ZProxy(ctx, name, sockets, new ZPump(), motdelafin, args); } /** * Creates a new low-level proxy for better performances. * * @param ctx the context used for the proxy. * Possibly null, in this case a new context will be created and automatically destroyed afterwards. * @param name the name of the proxy. Possibly null. * @param selector the creator of the selector used for the internal polling. Not null. * @param sockets the sockets creator of the proxy. Not null. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * * @return the created proxy. * @deprecated use {@link #newProxy(ZContext, String, Proxy, String, Object...)} instead. */ // creates a new low-level proxy @Deprecated public static ZProxy newProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets, String motdelafin, Object... args) { return newProxy(ctx, name, sockets, motdelafin, args); } /** * Creates a new low-level proxy for better performances. * * @param ctx the context used for the proxy. * Possibly null, in this case a new context will be created and automatically destroyed afterwards. * @param name the name of the proxy. Possibly null. * @param sockets the sockets creator of the proxy. Not null. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * * @return the created proxy. */ public static ZProxy newProxy(ZContext ctx, String name, Proxy sockets, String motdelafin, Object... args) { return new ZProxy(ctx, name, sockets, new ZmqPump(), motdelafin, args); } /** * Starts the proxy. * * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the read status */ public String start(boolean sync) { return command(START, sync); } /** * Pauses the proxy. * A paused proxy will cease processing messages, causing * them to be queued up and potentially hit the high-water mark on the * frontend or backend socket, causing messages to be dropped, or writing * applications to block. * * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the read status */ public String pause(boolean sync) { return command(PAUSE, sync); } /** * Stops the proxy. * * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the read status */ public String stop(boolean sync) { return command(STOP, sync); } /** * Sends a command message to the proxy actor. * Can be useful for programmatic interfaces. * Does not works with commands {@link #CONFIG CONFIG} and {@link #RESTART RESTART}. * * @param command the command to execute. Not null. * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the read status */ public String command(String command, boolean sync) { Utils.checkArgument( !CONFIG.equals(command), "CONFIG is not useable with that API. Please use configure(ZMsg) method"); Utils.checkArgument( !RESTART.equals(command), "RESTART is not useable with that API. Please use restart(ZMsg) method"); if (STATUS.equals(command)) { return status(sync); } if (EXIT.equals(command)) { return exit(); } // consume the status in the pipe String status = recvStatus(); if (agent.send(command)) { // the pipe is refilled if (sync) { status = status(true); } } return status; } /** * Sends a command message to the proxy actor. * Can be useful for programmatic interfaces. * Does not works with commands {@link Command#CONFIG CONFIG} and {@link Command#RESTART RESTART}. * * @param command the command to execute. * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the read state */ public State command(Command command, boolean sync) { return State.valueOf(command(command.name(), sync)); } /** * Sends a command message to the proxy actor. * Can be useful for programmatic interfaces. * Works only with commands {@link Command#CONFIG CONFIG} and {@link Command#RESTART RESTART}. * * @param command the command to execute. * @param msg the custom message to transmit. * @param sync true to read the status in synchronous way, false for asynchronous mode * @return the response message */ public ZMsg command(Command command, ZMsg msg, boolean sync) { if (command == Command.CONFIG) { return configure(msg); } if (command == Command.RESTART) { String status = restart(msg); msg = new ZMsg(); msg.add(status); return msg; } return null; } /** * Configures the proxy. * The distant side has to send back one (1) mandatory response message. * * @param msg the custom message sent as configuration tip * @return the mandatory response message of the configuration. */ public ZMsg configure(ZMsg msg) { msg.addFirst(CONFIG); if (agent.send(msg)) { // consume the status in the pipe recvStatus(); ZMsg reply = agent.recv(); assert (reply != null); // refill the pipe with status agent.send(STATUS); return reply; } return null; } /** * Restarts the proxy. Stays alive. * * @param hot null to make a cold restart (closing then re-creation of the sockets) * or a configuration message to perform a configurable hot restart, */ public String restart(ZMsg hot) { ZMsg msg = new ZMsg(); msg.add(RESTART); final boolean cold = hot == null; if (cold) { msg.add(Boolean.toString(false)); } else { msg.add(Boolean.toString(true)); msg.append(hot); } String status = EXITED; if (agent.send(msg)) { status = status(false); } return status; } /** * Stops the proxy and exits. * * @param sync forced to true to read the status in synchronous way. * @return the read status. * @deprecated The call is synchronous: the sync parameter is ignored, * as it leads to many mistakes in case of a provided ZContext. */ @Deprecated public String exit(boolean sync) { return exit(); } /** * Stops the proxy and exits. * The call is synchronous. * * @return the read status. * */ public String exit() { agent.send(EXIT); exit.awaitSilent(); agent.close(); return EXITED; } /** * Inquires for the status of the proxy. * This call is synchronous. */ public String status() { return status(true); } /** * Inquires for the status of the proxy. * * @param sync true to read the status in synchronous way, false for asynchronous mode. * If false, you get the last cached status of the proxy */ public String status(boolean sync) { if (exit.isExited()) { return EXITED; } try { String status = recvStatus(); if (agent.send(STATUS) && sync) { // wait for the response to emulate sync status = recvStatus(); // AND refill a status if (EXITED.equals(status) || !agent.send(STATUS)) { return EXITED; } } return status; } catch (ZMQException e) { return EXITED; } } // receives the last known state of the proxy private String recvStatus() { if (!agent.sign()) { return EXITED; } // receive the status response final ZMsg msg = agent.recv(); if (msg == null) { return EXITED; } String status = msg.popString(); msg.destroy(); return status; } /** * Binary inquiry for the status of the proxy. */ public boolean isStarted() { return started(); } /** * Binary inquiry for the status of the proxy. */ public boolean started() { String status = status(true); return STARTED.equals(status); } // to handle commands in a more java-centric way public enum Command { START, PAUSE, STOP, RESTART, EXIT, STATUS, CONFIG } // commands for the control pipe private static final String START = Command.START.name(); private static final String PAUSE = Command.PAUSE.name(); private static final String STOP = Command.STOP.name(); private static final String RESTART = Command.RESTART.name(); private static final String EXIT = Command.EXIT.name(); private static final String STATUS = Command.STATUS.name(); private static final String CONFIG = Command.CONFIG.name(); // to handle states in a more java-centric way public enum State { ALIVE, STARTED, PAUSED, STOPPED, EXITED } // state responses from the control pipe public static final String STARTED = State.STARTED.name(); public static final String PAUSED = State.PAUSED.name(); public static final String STOPPED = State.STOPPED.name(); public static final String EXITED = State.EXITED.name(); // defines the very first time where no command changing the state has been issued public static final String ALIVE = State.ALIVE.name(); private static final AtomicInteger counter = new AtomicInteger(); // the endpoint to the distant proxy actor private final ZAgent agent; // the synchronizer for exiting private final Exit exit; /** * Creates a new unnamed proxy. * * @param selector the creator of the selector used for the proxy. * @param creator the creator of the sockets of the proxy. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * @deprecated use {@link #ZProxy(Proxy, String, Object...)} instead. */ @Deprecated public ZProxy(SelectorCreator selector, Proxy creator, String motdelafin, Object... args) { this(creator, motdelafin, args); } /** * Creates a new unnamed proxy. * * @param creator the creator of the sockets of the proxy. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. */ public ZProxy(Proxy creator, String motdelafin, Object... args) { this(null, creator, null, motdelafin, args); } /** * Creates a new named proxy. * * @param name the name of the proxy (used in threads naming). * @param selector the creator of the selector used for the proxy. * @param creator the creator of the sockets of the proxy. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * @deprecated use {@link #ZProxy(String, Proxy, String, Object...)} instead. */ @Deprecated public ZProxy(String name, SelectorCreator selector, Proxy creator, String motdelafin, Object... args) { this(name, creator, motdelafin, args); } /** * Creates a new named proxy. * * @param name the name of the proxy (used in threads naming). * @param creator the creator of the sockets of the proxy. * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. */ public ZProxy(String name, Proxy creator, String motdelafin, Object... args) { this(name, creator, null, motdelafin, args); } /** * Creates a new named proxy. * * @param ctx the main context used. * If null, a new context will be created and closed at the stop of the operation. * If not null, it is the responsibility of the call to close it. * @param name the name of the proxy (used in threads naming). * @param selector the creator of the selector used for the proxy. * @param sockets the creator of the sockets of the proxy. * @param pump the pump used for the proxy * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. * @deprecated use {@link #ZProxy(ZContext, String, Proxy, Pump, String, Object...)} instead. */ @Deprecated public ZProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets, Pump pump, String motdelafin, Object... args) { this(ctx, name, sockets, pump, motdelafin, args); } /** * Creates a new named proxy. A new context will be created and closed at the stop of the operation. * * @param name the name of the proxy (used in threads naming). * @param sockets the creator of the sockets of the proxy. * @param pump the pump used for the proxy * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. */ public ZProxy(String name, Proxy sockets, Pump pump, String motdelafin, Object... args) { this(null, name, sockets, pump, motdelafin, args); } /** * Creates a new named proxy. * * @param ctx the main context used. * If null, a new context will be created and closed at the stop of the operation. * If not null, it is the responsibility of the call to close it. * @param name the name of the proxy (used in threads naming). * @param sockets the creator of the sockets of the proxy. * @param pump the pump used for the proxy * @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism. * @param args an optional array of arguments that will be passed at the creation. */ public ZProxy(ZContext ctx, String name, Proxy sockets, Pump pump, String motdelafin, Object... args) { super(); // arguments parsing if (pump == null) { pump = new ZmqPump(); } int count = 1; count += args.length; Object[] vars = new Object[count]; vars[0] = sockets; Actor shadow = null; // copy the arguments and retrieve the last optional shadow given in input for (int index = 0; index < args.length; ++index) { Object arg = args[index]; if (arg instanceof Actor) { shadow = (Actor) arg; } vars[index + 1] = arg; } // handle the actor int id = counter.incrementAndGet(); Actor actor = new ProxyActor(name, pump, id); if (shadow != null) { actor = new ZActor.Duo(actor, shadow); } ZActor zactor = new ZActor(ctx, actor, motdelafin, vars); agent = zactor.agent(); // NB: the zactor is also its own agent exit = zactor.exit(); } // defines a pump that will flow messages from one socket to another public interface Pump { /** * Transfers a message from one source to one destination, with an optional capture. * * @param src the plug of the source socket * @param source the socket where to receive the message from. * @param capture the optional sockets where to send the message to. Possibly null. * @param dst the plug of the destination socket * @param destination the socket where to send the message to. * * @return false in case of error or interruption, true if successfully transferred the message */ boolean flow(Plug src, Socket source, Socket capture, Plug dst, Socket destination); } // acts in background to proxy messages private static final class ProxyActor extends ZActor.SimpleActor { // the states container of the proxy private static final class State { @Override public String toString() { return "State [alive=" + alive + ", started=" + started + ", paused=" + paused + ", restart=" + restart + ", hot=" + hot + "]"; } // are we alive ? private boolean alive = false; // are we started ? private boolean started = false; // are we paused ? private boolean paused = false; // controls creation of a new agent if asked of a cold restart private boolean restart = false; // one-shot configuration for hot restart private ZMsg hot; } // the state of the proxy private final State state = new State(); // used to transfer message from one socket to another private final Pump transport; // the nice name of the proxy private final String name; // the provider of the sockets private Proxy provider; // the sockets creator user arguments private Object[] args; private Socket frontend; private Socket backend; private Socket capture; // creates a new Proxy actor. public ProxyActor(String name, Pump transport, int id) { if (name == null) { // default basic name this.name = String.format("ZProxy-%sd", id); } else { this.name = name; } this.transport = transport; } @Override public String premiere(Socket pipe) { ZMsg reply = new ZMsg(); reply.add(ALIVE); reply.send(pipe); return name; } // creates the sockets before the start of the proxy @Override public List createSockets(ZContext ctx, Object... args) { provider = (Proxy) args[0]; this.args = new Object[args.length - 1]; System.arraycopy(args, 1, this.args, 0, this.args.length); frontend = provider.create(ctx, Plug.FRONT, this.args); capture = provider.create(ctx, Plug.CAPTURE, this.args); backend = provider.create(ctx, Plug.BACK, this.args); assert (frontend != null); assert (backend != null); return Arrays.asList(frontend, backend); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { // init the state machine state.alive = true; state.restart = false; } // Process a control message @Override public boolean backstage(Socket pipe, ZPoller poller, int events) { assert (state.hot == null); String cmd = pipe.recvStr(); // a message has been received from the API if (START.equals(cmd)) { if (start(poller)) { return status().send(pipe); } // unable to start the proxy, exit state.restart = false; return false; } else if (STOP.equals(cmd)) { stop(); return status().send(pipe); } else if (PAUSE.equals(cmd)) { pause(poller, true); return status().send(pipe); } else if (RESTART.equals(cmd)) { String val = pipe.recvStr(); boolean hot = Boolean.parseBoolean(val); return restart(pipe, hot); } else if (STATUS.equals(cmd)) { return status().send(pipe); } else if (CONFIG.equals(cmd)) { ZMsg cfg = ZMsg.recvMsg(pipe); boolean rc = provider.configure(pipe, cfg, frontend, backend, capture, args); cfg.destroy(); return rc; } else if (EXIT.equals(cmd)) { // stops the proxy and the agent. // the status will be sent at the end of the loop state.restart = false; } else { return provider.custom(pipe, cmd, frontend, backend, capture, args); } return false; } // returns the status private ZMsg status() { ZMsg reply = new ZMsg(); if (!state.alive) { reply.add(EXITED); } else if (state.paused) { reply.add(PAUSED); } else if (state.started) { reply.add(STARTED); } else { reply.add(STOPPED); } return reply; } // starts the proxy sockets private boolean start(ZPoller poller) { boolean success = true; if (!state.started) { try { success = false; success |= provider.configure(frontend, Plug.FRONT, args); success |= provider.configure(backend, Plug.BACK, args); success |= provider.configure(capture, Plug.CAPTURE, args); state.started = true; } catch (RuntimeException | IOException e) { e.printStackTrace(); // unable to configure proxy, exit state.restart = false; state.started = false; return false; } } pause(poller, false); return success; } // pauses the proxy sockets private boolean pause(ZPoller poller, boolean pause) { state.paused = pause; if (pause) { poller.unregister(frontend); poller.unregister(backend); // TODO why not a mechanism for eventually flushing the sockets during the pause? } else { poller.register(frontend, ZPoller.POLLIN); poller.register(backend, ZPoller.POLLIN); // Now Wait also until there are either requests or replies to process. } return true; } private boolean stop() { // restart the actor in stopped state state.started = false; state.paused = false; // close connections state.restart = true; return true; } // handles the restart command in both modes private boolean restart(Socket pipe, boolean hot) { state.restart = true; if (hot) { assert (provider != null); state.hot = ZMsg.recvMsg(pipe); // continue with the same agent return true; } else { // stop the loop and restart a new agent // with the same started state // the next loop will refill the updated status return false; } } @Override public long looping(Socket pipe, ZPoller poller) { state.hot = null; return super.looping(pipe, poller); } // a message has been received for the proxy to process @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { if (socket == frontend) { // Process a request. return transport.flow(Plug.FRONT, frontend, capture, Plug.BACK, backend); } if (socket == backend) { // Process a reply. return transport.flow(Plug.BACK, backend, capture, Plug.FRONT, frontend); } return false; } @Override public boolean looped(Socket pipe, ZPoller poller) { if (state.restart) { if (state.hot == null) { // caught the cold restart return false; } else { // caught the hot restart ZMsg cfg = state.hot; state.hot = null; // we perform a cold restart if the provider says so boolean cold; ZMsg dup = cfg.duplicate(); try { cold = provider.restart(dup, frontend, Plug.FRONT, this.args); dup.destroy(); dup = cfg.duplicate(); cold |= provider.restart(dup, backend, Plug.BACK, this.args); dup.destroy(); dup = cfg.duplicate(); cold |= provider.restart(dup, capture, Plug.CAPTURE, this.args); dup.destroy(); cfg.destroy(); } catch (RuntimeException | IOException e) { e.printStackTrace(); state.restart = false; return false; } // cold restart means the loop has to stop. return !cold; } } return true; } // called in the proxy thread when it stopped. @Override public boolean destroyed(ZContext ctx, Socket pipe, ZPoller poller) { if (capture != null) { capture.close(); } state.alive = false; if (!state.restart) { status().send(pipe); } return state.restart; } } /** * A pump that reads a message as a whole before transmitting it. * It offers a way to transform messages for capture and destination. */ public static class ZPump implements Pump { private static final Identity IDENTITY = new Identity(); // the messages transformer private final Transformer transformer; // transforms one message into another public interface Transformer { /** * Transforms a ZMsg into another ZMsg. * Please note that this will be used during the message transfer, * so lengthy operations will have a cost on performances by definition. * If you return back another message than the one given in input, then this one has to be destroyed by you. * @param msg the message to transform * @param src the source plug * @param dst the destination plug * @return the transformed message */ ZMsg transform(ZMsg msg, Plug src, Plug dst); } private static class Identity implements Transformer { @Override public ZMsg transform(ZMsg msg, Plug src, Plug dst) { return msg; } } public ZPump() { this(null); } public ZPump(Transformer transformer) { super(); this.transformer = transformer == null ? IDENTITY : transformer; } @Override public boolean flow(Plug splug, Socket source, Socket capture, Plug dplug, Socket destination) { boolean success; // we read the whole message ZMsg msg = ZMsg.recvMsg(source); if (msg == null) { return false; } if (capture != null) { // Copy transformed message to capture socket if any message // TODO what if the transformer modifies or destroys the original message ? ZMsg cpt = transformer.transform(msg, splug, Plug.CAPTURE); // boolean destroy = !msg.equals(cpt); // TODO ?? which one boolean destroy = msg != cpt; success = cpt.send(capture, destroy); if (!success) { // not successful, but we can still try to send it to the destination } } ZMsg dst = transformer.transform(msg, splug, dplug); // we send the whole transformed message success = dst.send(destination); // finished msg.destroy(); return success; } } /** * A specialized transport for better transmission purposes * that will send each packets individually instead of the whole message. */ private static final class ZmqPump implements Pump { // transfers each message as a whole by sending each packet received to the capture socket @Override public boolean flow(Plug splug, Socket source, Socket capture, Plug dplug, Socket destination) { boolean rc; SocketBase src = source.base(); SocketBase dst = destination.base(); SocketBase cpt = capture == null ? null : capture.base(); // we transfer the whole message while (true) { // we read the packet Msg msg = src.recv(0); if (msg == null) { return false; } long more = src.getSocketOpt(zmq.ZMQ.ZMQ_RCVMORE); if (more < 0) { return false; } // Copy message to capture socket if any packet if (cpt != null) { Msg ctrl = new Msg(msg); rc = cpt.send(ctrl, more > 0 ? zmq.ZMQ.ZMQ_SNDMORE : 0); if (!rc) { // not successful, but we can still try to send it to the destination } } // we send the packet rc = dst.send(msg, more > 0 ? zmq.ZMQ.ZMQ_SNDMORE : 0); if (!rc) { return false; } if (more == 0) { break; } } return true; } } } jeromq-0.6.0/src/main/java/org/zeromq/ZSocket.java000066400000000000000000000173541455771126300220240ustar00rootroot00000000000000package org.zeromq; import java.util.concurrent.atomic.AtomicBoolean; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; /** * ZeroMQ sockets present an abstraction of an asynchronous message queue, with the exact queuing * semantics depending on the socket type in use. Where conventional sockets transfer streams of * bytes or discrete datagrams, ZeroMQ sockets transfer discrete messages. *

* ZeroMQ sockets being asynchronous means that the timings of the physical connection setup and * tear down, reconnect and effective delivery are transparent to the user and organized by ZeroMQ * itself. Further, messages may be queued in the event that a peer is unavailable to receive them. *

*/ public class ZSocket implements AutoCloseable { private final SocketBase socketBase; private final AtomicBoolean isClosed = new AtomicBoolean(false); /** * Create a ZeroMQ socket * * @param socketType ZeroMQ socket type */ public ZSocket(final int socketType) { socketBase = ManagedContext.getInstance().createSocket(socketType); } /** * Create a ZeroMQ socket * * @param socketType ZeroMQ Socket type */ public ZSocket(final SocketType socketType) { this(socketType.type()); } /** * Retrieve the socket type for the current 'socket'. The socket type is specified at socket * creation time and cannot be modified afterwards. * * @return the socket's type. */ public SocketType getSocketType() { return SocketType.type(getType()); } /** * Retrieve the socket type for the current 'socket'. The socket type is specified at socket * creation time and cannot be modified afterwards. * * @see ZSocket#getSocketType() * @return the socket's type. */ public int getType() { return (int) getOption(ZMQ.ZMQ_TYPE); } /** * Creates an endpoint for accepting connections and binds to it. *

* The endpoint argument is a string consisting of two parts as follows: transport ://address. The * transport part specifies the underlying transport protocol to use. The meaning of the address * part is specific to the underlying transport protocol selected. *

* * @param endpoint the endpoint to bind to * @return returns true if bind to the endpoint was successful */ public boolean bind(final String endpoint) { final boolean result = socketBase.bind(endpoint); mayRaise(); return result; } /** * Stop accepting connections on a socket. *

* Shall unbind from the endpoint specified by the endpoint argument. *

* * @param endpoint the endpoint to unbind from * @return returns true if unbind to the endpoint was successful */ public boolean unbind(final String endpoint) { final boolean result = socketBase.bind(endpoint); mayRaise(); return result; } /** * Connects the socket to an endpoint and then accepts incoming connections on that endpoint. *

* The endpoint is a string consisting of a transport :// followed by an address. The transport * specifies the underlying protocol to use. The address specifies the transport-specific address * to connect to. *

* * @param endpoint the endpoint to connect to * @return returns true if connecting to the endpoint was successful */ public boolean connect(final String endpoint) { final boolean result = socketBase.connect(endpoint); mayRaise(); return result; } /** * Disconnecting a socket from an endpoint. * * @param endpoint the endpoint to disconnect from * @return returns true if disconnecting to endpoint was successful */ public boolean disconnect(final String endpoint) { final boolean result = socketBase.termEndpoint(endpoint); mayRaise(); return result; } /** * Returns a boolean value indicating if the multipart message currently being read from the * {@code Socket} and has more message parts to follow. If there are no message parts to follow or * if the message currently being read is not a multipart message a value of false shall be * returned. Otherwise, a value of true shall be returned. * * @return true if there are more messages to receive. */ public final boolean hasReceiveMore() { return (int) getOption(ZMQ.ZMQ_RCVMORE) == 1; } public void subscribe(byte[] topic) { setOption(ZMQ.ZMQ_SUBSCRIBE, topic); } public void subscribe(String topic) { setOption(ZMQ.ZMQ_SUBSCRIBE, topic.getBytes(ZMQ.CHARSET)); } public void unsubscribe(byte[] topic) { setOption(ZMQ.ZMQ_UNSUBSCRIBE, topic); } public void unsubscribe(String topic) { setOption(ZMQ.ZMQ_UNSUBSCRIBE, topic.getBytes(ZMQ.CHARSET)); } public int send(byte[] b) { return send(b, 0); } public int send(byte[] b, int flags) { final Msg msg = new Msg(b); if (socketBase.send(msg, flags)) { return msg.size(); } mayRaise(); return -1; } public int sendMessage(Msg msg, int flags) { if (socketBase.send(msg, flags)) { return msg.size(); } mayRaise(); return -1; } public int sendMessage(Msg msg) { return sendMessage(msg, 0); } /** * Send a frame * * @param frame * @param flags * @return return true if successful */ public boolean sendFrame(ZFrame frame, int flags) { final byte[] data = frame.getData(); final Msg msg = new Msg(data); if (socketBase.send(msg, flags)) { return true; } mayRaise(); return false; } public boolean sendMessage(ZMsg message) { ZFrame frame = message.pop(); boolean rc = false; while (frame != null) { rc = sendFrame(frame, !message.isEmpty() ? ZMQ.ZMQ_MORE : 0); if (!rc) { break; } frame = message.pop(); } return rc; } public int sendStringUtf8(String str) { return sendStringUtf8(str, 0); } public int sendStringUtf8(String str, int flags) { final byte[] b = str.getBytes(ZMQ.CHARSET); return send(b, flags); } public byte[] receive() { return receive(0); } public byte[] receive(int flags) { final Msg msg = socketBase.recv(flags); if (msg == null) { return null; } return msg.data(); } public Msg receiveMessage() { return socketBase.recv(0); } public Msg receiveMessage(int flags) { return socketBase.recv(flags); } public String receiveStringUtf8() { return receiveStringUtf8(0); } public String receiveStringUtf8(int flags) { final byte[] b = receive(flags); return new String(b, ZMQ.CHARSET); } private void mayRaise() { final int errno = socketBase.errno(); if (errno != 0 && errno != ZError.EAGAIN) { throw new ZMQException(errno); } } private void setOption(int option, Object value) { socketBase.setSocketOpt(option, value); } private Object getOption(int option) { return socketBase.getSocketOptx(option); } /** * {@inheritDoc} */ @Override public void close() { if (isClosed.compareAndSet(false, true)) { ManagedContext.getInstance().destroy(socketBase); } } } jeromq-0.6.0/src/main/java/org/zeromq/ZStar.java000066400000000000000000000634711455771126300215060ustar00rootroot00000000000000package org.zeromq; import java.nio.channels.Selector; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread.IAttachedRunnable; import zmq.util.Objects; import zmq.util.function.BiFunction; /** * First implementation for the base of a remotely controlled background service for 0MQ. * *

A side or endpoint designates the same thing: the thread where lives one of the two parts of the system.

*

A {@link ZStar Star} has 2 sides (with a trial to use theater terms for fun (: and hopefully clarity :)

*
    *
  • the Corbeille side, or control side *
    * This is where one can {@link ZAgent#send(ZMsg) send} and {@link ZAgent#recv() receive} control messages to the underneath star via the ZStar agent. *
    * The ZStar lives on this side and is a way to safely communicate with the distant provided star. *
    * Note: Corbeille is a french word designing the most privileged places in the theater where the King was standing, * 1st floor, just above the orchestra (Wikipedia...).
  • *
  • the Plateau side, or background side where all the performances are made by the provided star. *
    * The provided star is living on the Plateau. *
    * The Plateau is made of the Stage where effective acting takes place and of the Wings * where the provided star can perform control decisions to stop or continue the acting. *
    * From this side, the work is done via callbacks using the template-method pattern, applied with interfaces (?)
  • *
*

Communication between the 2 sides is ensured by {@link #pipe() pipes} that SHALL NEVER be mixed between background and control sides. *
Instead, each side use its own pipe to communicate with the other one, transmitting only ØMQ messages.

*

The main purpose of this class is to ease this separation and to provide contracts, responsibilities and action levers for each side. *
* For example, instead of using pipe() which is useful to have more control, fast users would want to call {@link #agent()} to get the agent * or directly call {@link #send(ZMsg)} or {@link #recv()} from the ZStar as the ZStar is itself an agent!

* *

The ZStar takes care of the establishment of the background processing, calling the provided star * at appropriate times. It can also manage the {@link ZAgent#sign() exited} state on the control side, * if providing a non-null "Mot de la Fin". *
* It also takes care of the closing of sockets and context if it had to create one.

* *

A {@link Star star} is basically a contract interface that anyone who uses this ZStar to create a background service SHALL comply to.

*

PS: Je sais qu'il y a une différence entre acteur et comédien :)

*

PPS: I know nothing about theater!

* *

For an example of code, please refer to the {@link ZActor} source code.

* */ // remote controlled background message processing API for 0MQ. public class ZStar implements ZAgent { /** * Contract interface when acting in plain sight. */ // contract interface for acting with the spot lights on public interface Star { /** * Called when the star is in the wings.
* Hint: Can be used to initialize the service, or ...
* Key point: no loop has started already. */ void prepare(); /** * Called when the star in on stage, just before acting.
* Hint: Can be used to poll events or get input/events from other sources, or ...
* Key point: a loop just started. * * @return the number of events to process */ int breathe(); /** * Where acting takes place ...
* Hint: Can be used to process the events or input acquired from the previous step, or ...
* Key point: in the middle of a loop.
* Decision: to act on the next loop or not * * @param events the number of events to process * @return true to continue till the end of the act, false to stop loopS here. */ boolean act(int events); /** * Called as an interval between each act.
* Hint: Can be used to perform decisions to continue next loop or not, or to send computed data to outputs, or ...
* Key point: at the end of a loop.
* Decision: to act on the next loop or not * * @return true to continue acting, false to stop loopS here. */ boolean entract(); /** * Does the star want to renew for a new performance ? * Hint: Can be used to perform decisions to continue looping or not, or to send computed data to outputs, or ...
* Key point: the inner looping mechanism just ended
* Decision: to exit or not * * @return true to restart acting, false to leave here */ boolean renews(); } /** * Utility class with callback for when the Star has finished its performances. */ public interface TimeTaker { /** * Called when the show is finished but no cleaning is still done. * Useful to make the background thread wait a little bit, for example. * * @param ctx the shadow context */ void party(ZContext ctx); } // party time easily done. Wait for the specified amount of time public static void party(long time, TimeUnit unit) { ZMQ.sleep(time, unit); } // contract for a creator of stars public interface Fortune extends TimeTaker { /** * This is the grand premiere! * Called when the star enters the plateau. * The show is about to begin. Inform the public about that. * Called before the creation of the first star and its sockets * * @param mic the pipe to the Corbeille side * @param args the arguments passed as parameters of the star constructor * * @return the name of the upcoming performance. */ String premiere(Socket mic, Object... args); /** * Creates a star. * * @param ctx the context used for the creation of the sockets * @param mic the pipe to the Corbeille side * @param sel the selector used for polling * @param count the number of times a star was created. * @param previous the previous star if any (null at the first creation) * @param args the arguments passed as parameters of the star constructor * * @return a new star is born! * * @deprecated use {@link #create(ZContext, Socket, int, Star, Object...)} instead. */ @Deprecated default Star create(ZContext ctx, Socket mic, Selector sel, int count, Star previous, Object... args) { return this.create(ctx, mic, count, previous, args); } /** * Creates a star. * * @param ctx the context used for the creation of the sockets * @param mic the pipe to the Corbeille side * @param count the number of times a star was created. * @param previous the previous star if any (null at the first creation) * @param args the arguments passed as parameters of the star constructor * * @return a new star is born! */ Star create(ZContext ctx, Socket mic, int count, Star previous, Object... args); /** * The show is over. * Called when the show is over. * * @param mic the pipe to the Corbeille side * @return true to allow to spread the word and close all future communications */ boolean interview(Socket mic); } /** * Control for the end of the remote operations. */ public interface Exit { /** * Causes the current thread to wait in blocking mode until the end of the remote operations. */ void awaitSilent(); /** * Causes the current thread to wait in blocking mode until the end of the remote operations, * unless the thread is interrupted. * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@linkplain Thread#interrupt interrupted} while waiting, *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted * while waiting */ void await() throws InterruptedException; /** * Causes the current thread to wait in blocking mode until the end of the remote operations, * unless the thread is interrupted, or the specified waiting time elapses. * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@linkplain Thread#interrupt interrupted} while waiting, *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * *

If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param timeout the maximum time to wait * @param unit the time unit of the {@code timeout} argument * @return {@code true} if the remote operations ended and {@code false} * if the waiting time elapsed before the remote operations ended * @throws InterruptedException if the current thread is interrupted * while waiting */ boolean await(long timeout, TimeUnit unit) throws InterruptedException; /** * Checks in non-blocking mode, if the remote operations have ended. * @return true if the runnable where the remote operations occurred if finished, otherwise false. */ boolean isExited(); } /** * Returns the Corbeille endpoint. * Can be used to send or receive control messages to the distant star via Backstage. * * @return the agent/mailbox used to send and receive control messages to and from the star. */ public ZAgent agent() { return agent; } /** * Returns the control of the proper exit of the remote operations. * @return a structure used for checking the end of the remote operations. */ public Exit exit() { return plateau; } /** * Creates a new ZStar. * * @param selector the creator of the selector used on the Plateau. * @param fortune the creator of stars on the Plateau * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star * @deprecated use {@link ZStar#ZStar(Fortune, String, Object...)} instead. */ @Deprecated public ZStar(SelectorCreator selector, Fortune fortune, String motdelafin, Object... bags) { this(fortune, motdelafin, bags); } /** * Creates a new ZStar. * * @param fortune the creator of stars on the Plateau * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star */ public ZStar(final Fortune fortune, String motdelafin, final Object... bags) { this((ZContext) null, fortune, motdelafin, bags); } /** * Creates a new ZStar. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param selector the creator of the selector used on the Plateau. * @param fortune the creator of stars on the Plateau * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star * @deprecated use {@link ZStar#ZStar(ZContext, Fortune, String, Object...)} instead. */ @Deprecated public ZStar(final ZContext context, final SelectorCreator selector, final Fortune fortune, String motdelafin, final Object... bags) { this(context, fortune, ZAgent.Creator::create, motdelafin, bags); } /** * Creates a new ZStar. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param fortune the creator of stars on the Plateau * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star */ public ZStar(final ZContext context, final Fortune fortune, String motdelafin, final Object... bags) { this(context, fortune, ZAgent.Creator::create, motdelafin, bags); } /** * Creates a new ZStar. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param selector the creator of the selector used on the Plateau. * @param fortune the creator of stars on the Plateau. Not null. * @param agent the creator of the agent. Not null. * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star * @deprecated use {@link ZStar#ZStar(ZContext, Fortune, BiFunction, String, Object...)} instead. */ @Deprecated public ZStar(final ZContext context, final SelectorCreator selector, final Fortune fortune, BiFunction agent, String motdelafin, final Object... bags) { this(context, fortune, agent, motdelafin, bags); } /** * Creates a new ZStar. * * @param context * the main context used. If null, a new context will be created * and closed at the stop of the operation. * If not null, it is the responsibility of the caller to close it. * * @param fortune the creator of stars on the Plateau. Not null. * @param agent the creator of the agent. Not null. * @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism. * @param bags the optional arguments that will be passed to the distant star */ public ZStar(final ZContext context, final Fortune fortune, BiFunction agent, String motdelafin, final Object... bags) { super(); Objects.requireNonNull(agent, "Agent creator has to be supplied"); Objects.requireNonNull(fortune, "Fortune has to be supplied"); // entering platform to load trucks // initialize the context ZContext chef = context; ZContext producer = null; if (chef == null) { // no context provided, create one chef = new ZContext(); // it will be destroyed, so this is the main context producer = chef; } // context of the star. never null ZContext context1 = chef; assert (context1 != null); // retrieve the last optional set and entourage given in input Set set = null; Entourage entourage = null; for (Object bag : bags) { if (bag instanceof Set) { set = (Set) bag; } if (bag instanceof Entourage) { entourage = (Entourage) bag; } } if (set == null) { set = new SimpleSet(); } final List train = new ArrayList<>(6 + bags.length); train.add(set); train.add(fortune); train.add(producer); train.add(entourage); train.add(motdelafin); // 5 mandatory wagons train.addAll(Arrays.asList(bags)); // now going to the plateau Socket phone = ZThread.fork(chef, plateau, train.toArray()); this.agent = agent.apply(phone, motdelafin); } // communicating agent with the star for the Corbeille side private final ZAgent agent; // distant runnable where acting takes place private final Plateau plateau = new Plateau(); /** * Creates a new agent for the star. * * @param phone the socket used to communicate with the star * @param secret the specific keyword indicating the death of the star and locking the agent. Null to override the lock mechanism. * @return the newly created agent for the star. */ @Deprecated protected ZAgent agent(Socket phone, String secret) { return ZAgent.Creator.create(phone, secret); } // the plateau where the acting will take place (stage and backstage), or // the forked runnable containing the loop processing all messages in the background private static final class Plateau implements IAttachedRunnable, Exit { private static final AtomicInteger shows = new AtomicInteger(); // id if unnamed private final int number = shows.incrementAndGet(); // waiting-flag for the end of the remote operations private final CountDownLatch exit = new CountDownLatch(1); @Override public void run(final Object[] train, final ZContext chef, final Socket mic) { final int mandat = 5; // end of a trip can be a bit messy... Fortune star = (Fortune) train[1]; final Entourage entourage = (Entourage) train[3]; final ZContext producer = (ZContext) train[2]; final Set set = (Set) train[0]; // the word informing the world that the plateau is closed and the star vanished final String gossip = (String) train[4]; // prune our mandatory transit variables from the arguments final Object[] bags = new Object[train.length - mandat]; System.arraycopy(train, mandat, bags, 0, bags.length); // leaving unloaded platform if (entourage != null) { entourage.breakaleg(chef, star, mic, bags); } // now entering artistic zone try { // Premiere ! String name = star.premiere(mic, bags); // put the name of the performance on the front door with lightnings set.lights(name, number); // star is entering the wings showMustGoOn(chef, set, mic, star, bags); // star is leaving the plateau } finally { try { // star is interviewed about this event boolean tell = star.interview(mic); if (tell && gossip != null) { // inform the Corbeille side of the future closing of the plateau and the vanishing of the star mic.send(gossip); } // we are not in a hurry at this point when cleaning up the remains of a good show ... star.party(chef); if (entourage != null) { entourage.party(chef); } // Sober again ... // show is over, time to close chef.close(); if (producer != null) { // this is a self-generated context, destroy it producer.close(); } } finally { exit.countDown(); } } } /******************************************************************************/ /* TAP TAP TAP | | TAP | | TAP | | TAP | | | | | | | | | | | | | | | | | | | |*/ /******************************************************************************/ // starts the performance private void showMustGoOn(final ZContext chef, final Set set, final Socket phone, final Fortune fortune, final Object... bags) { int shows = 0; /** on the spot lights, the star in only an actor **/ Star actor = null; /** double-while-loop enables the restarting of a new star for the same acting on the same stage **/ /// so existing sockets can be closed and recreated in order to perform a cold restart or a stop **/ do { actor = fortune.create(chef, phone, shows++, actor, bags); /** a new star is born! And an acting one! **/ actor.prepare(); /** actor is leaving the wings **/ while (!set.fire()) { /** actor decides when the act will begin **/ int events = actor.breathe(); /** perform a new act of the show **/ if (!actor.act(events)) { // Context has been shut down break; } /** end of the act, back to the wings **/ if (!actor.entract()) { // star has decided to stop acting break; } } } while (actor.renews()); // star is leaving the Plateau and the show } /******************************************************************************/ /* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |*/ /******************************************************************************/ // NB: never use green color on the stage floor of a french theater. Or something bad will happen... @Override public void awaitSilent() { try { exit.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } @Override public void await() throws InterruptedException { exit.await(); } @Override public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return exit.await(timeout, unit); } @Override public boolean isExited() { return exit.getCount() == 0; } } @Override public ZMsg recv() { return agent.recv(); } @Override public ZMsg recv(int timeout) { return agent.recv(timeout); } @Override public ZMsg recv(boolean wait) { return agent.recv(wait); } @Override public boolean send(ZMsg message) { return agent.send(message); } @Override public boolean send(ZMsg msg, boolean destroy) { return agent.send(msg, destroy); } @Override public boolean send(String word) { return agent.send(word); } @Override public boolean send(String word, boolean more) { return agent.send(word, more); } @Override public Socket pipe() { return agent.pipe(); } @Override public boolean sign() { return agent.sign(); } @Override public void close() { agent.close(); } public interface Set { /** * Puts the performance name on the front door with big lights. * @param name the name of the performance. * @param id the performance number. */ void lights(String name, int id); /** * Is the set on fire ? * @return true if it is time to leave the place. */ boolean fire(); } public static class SimpleSet implements Set { @Override public boolean fire() { return Thread.currentThread().isInterrupted(); } @Override public void lights(String name, int id) { if (name == null) { name = createDefaultName("Star-%d", id); } Thread.currentThread().setName(name); } public static String createDefaultName(final String format, final int id) { return String.format(format, id); } } /** * Utility class with calls surrounding the execution of the Star. */ public interface Entourage extends TimeTaker { /** * Called when the show is about to start. * Can be a useful point in the whole process from time to time. * * @param ctx the context provided in the creation step * @param fortune the creator of stars * @param phone the socket used to communicate with the Agent * @param bags the optional arguments that were passed at the creation */ void breakaleg(ZContext ctx, Fortune fortune, Socket phone, Object... bags); // well ... } } jeromq-0.6.0/src/main/java/org/zeromq/ZThread.java000066400000000000000000000063501455771126300217750ustar00rootroot00000000000000package org.zeromq; import org.zeromq.ZMQ.Error; import org.zeromq.ZMQ.Socket; import java.util.Locale; public class ZThread { private ZThread() { } public interface IAttachedRunnable { void run(Object[] args, ZContext ctx, Socket pipe); } public interface IDetachedRunnable { void run(Object[] args); } private static class ShimThread extends Thread { private ZContext ctx; private IAttachedRunnable attachedRunnable; private IDetachedRunnable detachedRunnable; private final Object[] args; private Socket pipe; protected ShimThread(ZContext ctx, IAttachedRunnable runnable, Object[] args, Socket pipe) { assert (ctx != null); assert (pipe != null); assert (runnable != null); this.ctx = ctx; this.attachedRunnable = runnable; this.args = args; this.pipe = pipe; this.setUncaughtExceptionHandler(ctx.getUncaughtExceptionHandler()); } public ShimThread(IDetachedRunnable runnable, Object[] args) { assert (runnable != null); this.detachedRunnable = runnable; this.args = args; } @Override public void run() { if (attachedRunnable != null) { try { attachedRunnable.run(args, ctx, pipe); } catch (ZMQException e) { if (e.getErrorCode() != Error.ETERM.getCode()) { throw e; } } ctx.destroy(); } else { detachedRunnable.run(args); } } } // -------------------------------------------------------------------------- // Create a detached thread. A detached thread operates autonomously // and is used to simulate a separate process. It gets no ctx, and no // pipe. public static void start(IDetachedRunnable runnable, Object... args) { // Prepare child thread Thread shim = new ShimThread(runnable, args); shim.setDaemon(true); shim.start(); } // -------------------------------------------------------------------------- // Create an attached thread. An attached thread gets a ctx and a PAIR // pipe back to its parent. It must monitor its pipe, and exit if the // pipe becomes unreadable. Returns pipe, or null if there was an error. public static Socket fork(ZContext ctx, IAttachedRunnable runnable, Object... args) { Socket pipe = ctx.createSocket(SocketType.PAIR); assert (pipe != null); pipe.bind(String.format(Locale.ENGLISH, "inproc://zctx-pipe-%d", pipe.hashCode())); // Connect child pipe to our pipe ZContext ccontext = ctx.shadow(); Socket cpipe = ccontext.createSocket(SocketType.PAIR); assert (cpipe != null); cpipe.connect(String.format(Locale.ENGLISH, "inproc://zctx-pipe-%d", pipe.hashCode())); // Prepare child thread Thread shim = new ShimThread(ccontext, runnable, args, cpipe); shim.start(); return pipe; } } jeromq-0.6.0/src/main/java/org/zeromq/ZTimer.java000066400000000000000000000067501455771126300216520ustar00rootroot00000000000000package org.zeromq; import zmq.util.Draft; /** * Manages set of timers. *

* Timers can be added with a given interval, when the interval of time expires after addition, handler method is executed with given arguments. * Timer is repetitive and will be executed over time until canceled. *

* This is a DRAFT class, and may change without notice. * @deprecated scheduled for removal in future release. Please use {@link org.zeromq.timer.ZTimer} instead */ @Draft @Deprecated public final class ZTimer { /** * Opaque representation of a timer. * @deprecated use {@link org.zeromq.timer.ZTimer.Timer} instead */ @Deprecated public static final class Timer { private final org.zeromq.timer.ZTimer.Timer delegate; Timer(org.zeromq.timer.ZTimer.Timer delegate) { this.delegate = delegate; } } /** * @deprecated use {@link org.zeromq.timer.TimerHandler} instead */ @Deprecated public interface Handler extends org.zeromq.timer.TimerHandler { } private final org.zeromq.timer.ZTimer timer = new org.zeromq.timer.ZTimer(); /** * Add timer to the set, timer repeats forever, or until cancel is called. * @param interval the interval of repetition in milliseconds. * @param handler the callback called at the expiration of the timer. * @param args the optional arguments for the handler. * @return an opaque handle for further cancel. */ public Timer add(long interval, Handler handler, Object... args) { if (handler == null) { return null; } return new Timer(timer.add(interval, handler, args)); } /** * Changes the interval of the timer. *

* This method is slow, canceling existing and adding a new timer yield better performance. * @param timer the timer to change the interval to. * @return true if set, otherwise false. * @deprecated use {@link org.zeromq.timer.ZTimer.Timer#setInterval(long)} instead */ @Deprecated public boolean setInterval(Timer timer, long interval) { return timer.delegate.setInterval(interval); } /** * Reset the timer. *

* This method is slow, canceling existing and adding a new timer yield better performance. * @param timer the timer to reset. * @return true if reset, otherwise false. * @deprecated use {@link org.zeromq.timer.ZTimer.Timer#reset()} instead */ @Deprecated public boolean reset(Timer timer) { return timer.delegate.reset(); } /** * Cancel a timer. * * @param timer the timer to cancel. * @return true if cancelled, otherwise false. * @deprecated use {@link org.zeromq.timer.ZTimer.Timer#cancel()} instead */ @Deprecated public boolean cancel(Timer timer) { return timer.delegate.cancel(); } /** * Returns the time in millisecond until the next timer. * * @return the time in millisecond until the next timer. */ public long timeout() { return timer.timeout(); } /** * Execute the timers. * * @return the number of timers triggered. */ public int execute() { return timer.execute(); } /** * Sleeps until at least one timer can be executed and execute the timers. * * @return the number of timers triggered. */ public int sleepAndExecute() { return timer.sleepAndExecute(); } } jeromq-0.6.0/src/main/java/org/zeromq/package-info.java000066400000000000000000000006651455771126300227630ustar00rootroot00000000000000/** * Provides high-level bindings for ØMQ. *
* This is the java equivalent of CZMQ project. *
* Classes of this package tend to achieve these goals: *

    *
  • To wrap the ØMQ core API in semantics that lead to shorter, more readable applications.
  • *
  • To provide a space for development of more sophisticated API semantics.
  • *
*/ package org.zeromq; jeromq-0.6.0/src/main/java/org/zeromq/proto/000077500000000000000000000000001455771126300207305ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/zeromq/proto/ZNeedle.java000066400000000000000000000136011455771126300231220ustar00rootroot00000000000000package org.zeromq.proto; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.zeromq.ZFrame; import zmq.util.Draft; import zmq.util.Utils; import zmq.util.Wire; import zmq.util.function.BiFunction; /** * Needle for de/serialization of data within a frame. *

* This is a DRAFT class, and may change without notice. */ @Draft public final class ZNeedle { private final ByteBuffer needle; // Read/write pointer for serialization public ZNeedle(ZFrame frame) { this(frame.getData()); } private ZNeedle(byte[] data) { needle = ByteBuffer.wrap(data); } private void checkAvailable(int size) { Utils.checkArgument(needle.position() + size <= needle.limit(), () -> "Unable to handle " + size + " bytes"); } private void forward(int size) { needle.position(needle.position() + size); } private T get(BiFunction getter, int size) { T value = getter.apply(needle, needle.position()); forward(size); return value; } // Put a 1-byte number to the frame public void putNumber1(int value) { checkAvailable(1); needle.put((byte) (value & 0xff)); } // Get a 1-byte number to the frame // then make it unsigned public int getNumber1() { checkAvailable(1); int value = needle.get(needle.position()) & 0xff; forward(1); return value; } // Put a 2-byte number to the frame public void putNumber2(int value) { checkAvailable(2); Wire.putUInt16(needle, value); } // Get a 2-byte number to the frame public int getNumber2() { checkAvailable(2); return get(Wire::getUInt16, 2); } // Put a 4-byte number to the frame public void putNumber4(int value) { checkAvailable(4); Wire.putUInt32(needle, value); } // Get a 4-byte number to the frame // then make it unsigned public int getNumber4() { checkAvailable(4); return get(Wire::getUInt32, 4); } // Put a 8-byte number to the frame public void putNumber8(long value) { checkAvailable(8); Wire.putUInt64(needle, value); } // Get a 8-byte number to the frame public long getNumber8() { checkAvailable(8); return get(Wire::getUInt64, 8); } // Put a block to the frame public void putBlock(byte[] value, int size) { needle.put(value, 0, size); } public byte[] getBlock(int size) { checkAvailable(size); byte[] value = new byte[size]; needle.get(value); return value; } // Put a string to the frame public void putShortString(String value) { checkAvailable(value.length() + 1); Wire.putShortString(needle, value); } // Get a string from the frame public String getShortString() { String value = Wire.getShortString(needle, needle.position()); forward(value.length() + 1); return value; } public void putLongString(String value) { checkAvailable(value.length() + 4); Wire.putLongString(needle, value); } // Get a long string from the frame public String getLongString() { String value = Wire.getLongString(needle, needle.position()); forward(value.length() + 4); return value; } // Put a string to the frame public void putString(String value) { if (value.length() > Byte.MAX_VALUE * 2 + 1) { putLongString(value); } else { putShortString(value); } } // Get a short string from the frame public String getString() { return getShortString(); } // Put a collection of strings to the frame public void putList(Collection elements) { if (elements == null) { putNumber1(0); } else { Utils.checkArgument(elements.size() < 256, "Collection has to be smaller than 256 elements"); putNumber1(elements.size()); for (String string : elements) { putString(string); } } } public List getList() { int size = getNumber1(); List list = new ArrayList<>(size); for (int idx = 0; idx < size; ++ idx) { list.add(getString()); } return list; } // Put a map of strings to the frame public void putMap(Map map) { if (map == null) { putNumber1(0); } else { Utils.checkArgument(map.size() < 256, "Map has to be smaller than 256 elements"); putNumber1(map.size()); for (Entry entry : map.entrySet()) { if (entry.getKey().contains("=")) { throw new IllegalArgumentException("Keys cannot contain '=' sign. " + entry); } if (entry.getValue().contains("=")) { throw new IllegalArgumentException("Values cannot contain '=' sign. " + entry); } String val = entry.getKey() + "=" + entry.getValue(); putString(val); } } } public Map getMap() { int size = getNumber1(); Map map = new HashMap<>(size); for (int idx = 0; idx < size; ++idx) { String[] kv = getString().split("="); assert (kv.length == 2); map.put(kv[0], kv[1]); } return map; } @Override public String toString() { return "ZNeedle [position=" + needle.position() + ", ceiling=" + needle.limit() + "]"; } } jeromq-0.6.0/src/main/java/org/zeromq/proto/ZPicture.java000066400000000000000000000403331455771126300233430ustar00rootroot00000000000000package org.zeromq.proto; import java.util.Locale; import java.util.regex.Pattern; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMQException; import org.zeromq.ZMsg; import zmq.ZError; import zmq.util.Draft; /** * De/serialization of data within a message. *

* This is a DRAFT class, and may change without notice. */ @Draft public class ZPicture { private static final Pattern FORMAT = Pattern.compile("[i1248sbcfz]*m?"); private static final Pattern BINARY_FORMAT = Pattern.compile("[1248sSbcf]*m?"); /** * Creates a binary encoded 'picture' message to the socket (or actor), so it can be sent. * The arguments are encoded in a binary format that is compatible with zproto, and * is designed to reduce memory allocations. * * @param picture The picture argument is a string that defines the * type of each argument. Supports these argument types: *

* * * * * * * * * * * * * *
Types of arguments
patternjava typezproto type
1inttype = "number" size = "1"
2inttype = "number" size = "2"
4longtype = "number" size = "3"
8longtype = "number" size = "4"
sString, 0-255 charstype = "string"
SString, 0-2^32-1 charstype = "longstr"
bbyte[], 0-2^32-1 bytestype = "chunk"
cbyte[], 0-2^32-1 bytestype = "chunk"
fZFrametype = "frame"
mZMsgtype = "msg" Has to be the last element of the picture
* @param args Arguments according to the picture * @return a new {@link ZMsg} that encode the arguments * @api.note Does not change or take ownership of any arguments. */ @Draft public ZMsg msgBinaryPicture(String picture, Object... args) { if (!BINARY_FORMAT.matcher(picture).matches()) { throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(), ZError.EPROTO); } ZMsg msg = new ZMsg(); // Pass 1: calculate total size of data frame int frameSize = 0; for (int index = 0; index < picture.length(); index++) { char pattern = picture.charAt(index); switch (pattern) { case '1': { frameSize += 1; break; } case '2': { frameSize += 2; break; } case '4': { frameSize += 4; break; } case '8': { frameSize += 8; break; } case 's': { String string = (String) args[index]; frameSize += 1 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0); break; } case 'S': { String string = (String) args[index]; frameSize += 4 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0); break; } case 'b': case 'c': { byte[] block = (byte[]) args[index]; frameSize += 4 + block.length; break; } case 'f': { ZFrame frame = (ZFrame) args[index]; msg.add(frame); break; } case 'm': { ZMsg other = (ZMsg) args[index]; if (other == null) { msg.add(new ZFrame((byte[]) null)); } else { msg.addAll(other); } break; } default: assert (false) : "invalid picture element '" + pattern + "'"; } } // Pass 2: encode data into data frame ZFrame frame = new ZFrame(new byte[frameSize]); ZNeedle needle = new ZNeedle(frame); for (int index = 0; index < picture.length(); index++) { char pattern = picture.charAt(index); switch (pattern) { case '1': { needle.putNumber1((int) args[index]); break; } case '2': { needle.putNumber2((int) args[index]); break; } case '4': { needle.putNumber4((int) args[index]); break; } case '8': { needle.putNumber8((long) args[index]); break; } case 's': { needle.putString((String) args[index]); break; } case 'S': { needle.putLongString((String) args[index]); break; } case 'b': case 'c': { byte[] block = (byte[]) args[index]; needle.putNumber4(block.length); needle.putBlock(block, block.length); break; } case 'f': case 'm': break; default: assert (false) : "invalid picture element '" + pattern + "'"; } } msg.addFirst(frame); return msg; } @Draft public boolean sendBinaryPicture(Socket socket, String picture, Object... args) { return msgBinaryPicture(picture, args).send(socket); } /** * Receive a binary encoded 'picture' message from the socket (or actor). * This method is similar to {@link org.zeromq.ZMQ.Socket#recv()}, except the arguments are encoded * in a binary format that is compatible with zproto, and is designed to * reduce memory allocations. * * @param picture The picture argument is a string that defines * the type of each argument. See {@link #sendBinaryPicture(Socket, String, Object...)} * for the supported argument types. * @return the picture elements as object array **/ @Draft public Object[] recvBinaryPicture(Socket socket, final String picture) { if (!BINARY_FORMAT.matcher(picture).matches()) { throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(), ZError.EPROTO); } ZFrame frame = ZFrame.recvFrame(socket); if (frame == null) { return null; } // Get the data frame ZNeedle needle = new ZNeedle(frame); Object[] results = new Object[picture.length()]; for (int index = 0; index < picture.length(); index++) { char pattern = picture.charAt(index); switch (pattern) { case '1': { results[index] = needle.getNumber1(); break; } case '2': { results[index] = needle.getNumber2(); break; } case '4': { results[index] = needle.getNumber4(); break; } case '8': { results[index] = needle.getNumber8(); break; } case 's': { results[index] = needle.getString(); break; } case 'S': { results[index] = needle.getLongString(); break; } case 'b': case 'c': { int size = needle.getNumber4(); results[index] = needle.getBlock(size); break; } case 'f': { // Get next frame off socket results[index] = ZFrame.recvFrame(socket); break; } case 'm': { // Get zero or more remaining frames results[index] = ZMsg.recvMsg(socket); break; } default: assert (false) : "invalid picture element '" + pattern + "'"; } } return results; } /** * Queues a 'picture' message to the socket (or actor), so it can be sent. * * @param picture The picture is a string that defines the type of each frame. * This makes it easy to send a complex multiframe message in * one call. The picture can contain any of these characters, * each corresponding to zero or one arguments: * *

* * * * * * * * * * * * * *
Types of arguments
i = int (stores signed integer)
1 = byte (stores 8-bit unsigned integer)
2 = int (stores 16-bit unsigned integer)
4 = long (stores 32-bit unsigned integer)
8 = long (stores 64-bit unsigned integer)
s = String
b = byte[]
c = byte[]
f = ZFrame
m = ZMsg (sends all frames in the ZMsg)Has to be the last element of the picture
z = sends zero-sized frame (0 arguments)
* Note that s, b, f and m are encoded the same way and the choice is * offered as a convenience to the sender, which may or may not already * have data in a ZFrame or ZMsg. Does not change or take ownership of * any arguments. *

* Also see {@link #recvPicture(Socket, String)}} how to recv a * multiframe picture. * @param args Arguments according to the picture * @return true if successful, false if sending failed for any reason */ @Draft public boolean sendPicture(Socket socket, String picture, Object... args) { if (!FORMAT.matcher(picture).matches()) { throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO); } ZMsg msg = new ZMsg(); for (int pictureIndex = 0, argIndex = 0; pictureIndex < picture.length(); pictureIndex++, argIndex++) { char pattern = picture.charAt(pictureIndex); switch (pattern) { case 'i': { msg.add(String.format(Locale.ENGLISH, "%d", (int) args[argIndex])); break; } case '1': { msg.add(String.format(Locale.ENGLISH, "%d", (0xff) & (int) args[argIndex])); break; } case '2': { msg.add(String.format(Locale.ENGLISH, "%d", (0xffff) & (int) args[argIndex])); break; } case '4': { msg.add(String.format(Locale.ENGLISH, "%d", (0xffffffff) & (int) args[argIndex])); break; } case '8': { msg.add(String.format(Locale.ENGLISH, "%d", (long) args[argIndex])); break; } case 's': { msg.add((String) args[argIndex]); break; } case 'b': case 'c': { msg.add((byte[]) args[argIndex]); break; } case 'f': { msg.add((ZFrame) args[argIndex]); break; } case 'm': { ZMsg msgParm = (ZMsg) args[argIndex]; while (!msgParm.isEmpty()) { msg.add(msgParm.pop()); } break; } case 'z': { msg.add((byte[]) null); argIndex--; break; } default: assert (false) : "invalid picture element '" + pattern + "'"; } } return msg.send(socket, false); } /** * Receive a 'picture' message to the socket (or actor). * * * @param picture The picture is a string that defines the type of each frame. * This makes it easy to recv a complex multiframe message in * one call. The picture can contain any of these characters, * each corresponding to zero or one elements in the result: * *

* * * * * * * * * * * * *
Types of arguments
i = int (stores signed integer)
1 = int (stores 8-bit unsigned integer)
2 = int (stores 16-bit unsigned integer)
4 = long (stores 32-bit unsigned integer)
8 = long (stores 64-bit unsigned integer)
s = String
b = byte[]
f = ZFrame (creates zframe)
m = ZMsg (creates a zmsg with the remaing frames)
z = null, asserts empty frame (0 arguments)
* * Also see {@link #sendPicture(Socket, String, Object...)} how to send a * multiframe picture. * * @return the picture elements as object array */ @Draft public Object[] recvPicture(Socket socket, String picture) { if (!FORMAT.matcher(picture).matches()) { throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO); } Object[] elements = new Object[picture.length()]; for (int index = 0; index < picture.length(); index++) { char pattern = picture.charAt(index); switch (pattern) { case 'i': { elements[index] = Integer.valueOf(socket.recvStr()); break; } case '1': { elements[index] = (0xff) & Integer.parseInt(socket.recvStr()); break; } case '2': { elements[index] = (0xffff) & Integer.parseInt(socket.recvStr()); break; } case '4': { elements[index] = (0xffffffff) & Integer.parseInt(socket.recvStr()); break; } case '8': { elements[index] = Long.valueOf(socket.recvStr()); break; } case 's': { elements[index] = socket.recvStr(); break; } case 'b': case 'c': { elements[index] = socket.recv(); break; } case 'f': { elements[index] = ZFrame.recvFrame(socket); break; } case 'm': { elements[index] = ZMsg.recvMsg(socket); break; } case 'z': { ZFrame zeroFrame = ZFrame.recvFrame(socket); if (zeroFrame == null || zeroFrame.size() > 0) { throw new ZMQException("zero frame is not empty", ZError.EPROTO); } elements[index] = new ZFrame((byte[]) null); break; } default: assert (false) : "invalid picture element '" + pattern + "'"; } } return elements; } } jeromq-0.6.0/src/main/java/org/zeromq/proto/package-info.java000066400000000000000000000001261455771126300241160ustar00rootroot00000000000000/** *

Provides utility classes for ØMQ zproto.

*/ package org.zeromq.proto; jeromq-0.6.0/src/main/java/org/zeromq/timer/000077500000000000000000000000001455771126300207055ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/zeromq/timer/TimerHandler.java000066400000000000000000000004621455771126300241300ustar00rootroot00000000000000package org.zeromq.timer; import zmq.util.Draft; import zmq.util.Timers; /** * Called when the time has come to perform some action. * This is a DRAFT class, and may change without notice. */ @Draft public interface TimerHandler extends Timers.Handler { @Override void time(Object... args); } jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTicker.java000066400000000000000000000033321455771126300231240ustar00rootroot00000000000000package org.zeromq.timer; import org.zeromq.timer.ZTicket.Ticket; import org.zeromq.timer.ZTimer.Timer; import zmq.util.Draft; import zmq.util.function.Supplier; /** * Manages set of tickets and timers. *

* Tickets can be added with a given delay in milliseconds, * when the delay expires after addition, * handler method is executed with given arguments. *
* Ticket is NOT repetitive and will be executed once unless canceled. *

* Timers can be added with a given interval in milliseconds, * when the interval of time expires after addition, * handler method is executed with given arguments. *
* Timer is repetitive and will be executed over time until canceled. *

* This is a DRAFT class, and may change without notice. */ @Draft public final class ZTicker { private final ZTimer timer; private final ZTicket ticket; public ZTicker() { timer = new ZTimer(); ticket = new ZTicket(); } ZTicker(Supplier clock) { timer = new ZTimer(clock); ticket = new ZTicket(clock); } public Ticket addTicket(long interval, TimerHandler handler, Object... args) { return ticket.add(interval, handler, args); } public Timer addTimer(long interval, TimerHandler handler, Object... args) { return timer.add(interval, handler, args); } public long timeout() { long timer = this.timer.timeout(); long ticket = this.ticket.timeout(); if (timer < 0 || ticket < 0) { return Math.max(timer, ticket); } else { return Math.min(timer, ticket); } } public int execute() { return timer.execute() + ticket.execute(); } } jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTicket.java000066400000000000000000000136661455771126300231410ustar00rootroot00000000000000package org.zeromq.timer; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import zmq.util.Clock; import zmq.util.Draft; import zmq.util.Utils; import zmq.util.function.Supplier; /** * Manages set of tickets. *

* Ticket timers are very fast in the case where * you use a lot of timers (thousands), and frequently remove and add them. * The main use case is expiry timers for servers that handle many clients, * and which reset the expiry timer for each message received from a client. * Whereas normal timers perform poorly as the number of clients grows, the * cost of ticket timers is constant, no matter the number of clients *

* Tickets can be added with a given delay. *
* When the delay of time expires after addition, handler method is executed with given arguments. *
* Ticket is NOT repetitive and will be executed once unless canceled. *

* This class is not thread-safe *

* This is a DRAFT class, and may change without notice. */ @Draft public final class ZTicket { /** * Opaque representation of a ticket. */ public static final class Ticket implements Comparable { private final ZTicket parent; private final TimerHandler handler; private final Object[] args; private long start; private long delay; private boolean alive = true; private Ticket(ZTicket parent, long now, long delay, TimerHandler handler, Object... args) { assert (args != null); this.parent = parent; this.start = now; this.delay = delay; this.handler = handler; this.args = args; } /** * Resets the ticket. */ public void reset() { if (alive) { parent.sort = true; start = parent.now(); } } /** * Cancels a ticket. * @return true if cancelled, false if already cancelled. */ public boolean cancel() { if (alive) { alive = false; parent.sort = true; return true; } return false; } /** * Changes the delay of the ticket. * @param delay the new delay of the ticket. */ public void setDelay(long delay) { if (alive) { parent.sort = true; this.delay = delay; } } @Override public int compareTo(Ticket other) { if (alive) { if (other.alive) { return Long.compare(start - other.start, other.delay - delay); } return -1; } return other.alive ? 1 : 0; } } private final List tickets; private final Supplier clock; private boolean sort; public ZTicket() { this(() -> TimeUnit.NANOSECONDS.toMillis(Clock.nowNS())); } ZTicket(Supplier clock) { this(clock, new ArrayList<>()); } ZTicket(Supplier clock, List tickets) { this.clock = clock; this.tickets = tickets; } private long now() { return clock.get(); } private void insert(Ticket ticket) { sort = tickets.add(ticket); } /** * Add ticket to the set. * @param delay the expiration delay in milliseconds. * @param handler the callback called at the expiration of the ticket. * @param args the optional arguments for the handler. * @return an opaque handle for further cancel and reset. */ public Ticket add(long delay, TimerHandler handler, Object... args) { if (handler == null) { return null; } Utils.checkArgument(delay > 0, "Delay of a ticket has to be strictly greater than 0"); final Ticket ticket = new Ticket(this, now(), delay, handler, args); insert(ticket); return ticket; } /** * Returns the time in millisecond until the next ticket. * @return the time in millisecond until the next ticket. */ public long timeout() { if (tickets.isEmpty()) { return -1; } sortIfNeeded(); // Tickets are sorted, so check first ticket Ticket first = tickets.get(0); long time = first.start - now() + first.delay; if (time > 0) { return time; } else { return 0; } } /** * Execute the tickets. * @return the number of tickets triggered. */ public int execute() { int executed = 0; final long now = now(); sortIfNeeded(); Set cancelled = new HashSet<>(); for (Ticket ticket : this.tickets) { if (now - ticket.start < ticket.delay) { // tickets are ordered, not meeting the condition means the next ones do not as well break; } if (!ticket.alive) { // Dead ticket, let's continue cancelled.add(ticket); continue; } ticket.alive = false; cancelled.add(ticket); ticket.handler.time(ticket.args); ++executed; } for (int idx = tickets.size(); idx-- > 0; ) { Ticket ticket = tickets.get(idx); if (ticket.alive) { break; } cancelled.add(ticket); } this.tickets.removeAll(cancelled); cancelled.clear(); return executed; } private void sortIfNeeded() { if (sort) { sort = false; Collections.sort(tickets); } } } jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTimer.java000066400000000000000000000056671455771126300230000ustar00rootroot00000000000000package org.zeromq.timer; import zmq.util.Draft; import zmq.util.Timers; import zmq.util.function.Supplier; /** * Manages set of timers. *

* Timers can be added with a given interval, when the interval of time expires after addition, handler method is executed with given arguments. * Timer is repetitive and will be executed over time until canceled. *

* This is a DRAFT class, and may change without notice. */ @Draft public final class ZTimer { /** * Opaque representation of a timer. */ public static final class Timer { private final Timers.Timer delegate; Timer(Timers.Timer delegate) { this.delegate = delegate; } /** * Changes the interval of the timer. *

* This method is slow, canceling existing and adding a new timer yield better performance. * @param interval the new interval of the time. * @return true if set, otherwise false. */ public boolean setInterval(long interval) { return delegate.setInterval(interval); } /** * Reset the timer. *

* This method is slow, canceling existing and adding a new timer yield better performance. * @return true if reset, otherwise false. */ public boolean reset() { return delegate.reset(); } /** * Cancels a timer. * * @return true if cancelled, otherwise false. */ public boolean cancel() { return delegate.cancel(); } } private final Timers timer; public ZTimer() { timer = new Timers(); } ZTimer(Supplier clock) { timer = new Timers(clock); } /** * Add timer to the set, timer repeats forever, or until cancel is called. * @param interval the interval of repetition in milliseconds. * @param handler the callback called at the expiration of the timer. * @param args the optional arguments for the handler. * @return an opaque handle for further cancel. */ public Timer add(long interval, TimerHandler handler, Object... args) { if (handler == null) { return null; } return new Timer(timer.add(interval, handler, args)); } /** * Returns the time in millisecond until the next timer. * * @return the time in millisecond until the next timer. */ public long timeout() { return timer.timeout(); } /** * Execute the timers. * * @return the number of timers triggered. */ public int execute() { return timer.execute(); } /** * Sleeps until at least one timer can be executed and execute the timers. * * @return the number of timers triggered. */ public int sleepAndExecute() { return timer.sleepAndExecute(); } } jeromq-0.6.0/src/main/java/org/zeromq/timer/package-info.java000066400000000000000000000001261455771126300240730ustar00rootroot00000000000000/** *

Provides timing utility classes for ØMQ.

*/ package org.zeromq.timer; jeromq-0.6.0/src/main/java/org/zeromq/util/000077500000000000000000000000001455771126300205425ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/org/zeromq/util/ZData.java000066400000000000000000000075511455771126300224200ustar00rootroot00000000000000package org.zeromq.util; import java.io.PrintStream; import java.util.Arrays; import org.zeromq.ZMQ; public class ZData { private static final String HEX_CHAR = "0123456789ABCDEF"; private final byte[] data; public ZData(byte[] data) { this.data = data; } /** * String equals. * Uses String compareTo for the comparison (lexigraphical) * @param str String to compare with data * @return True if data matches given string */ public boolean streq(String str) { return streq(data, str); } /** * String equals. * Uses String compareTo for the comparison (lexigraphical) * @param str String to compare with data * @param data the binary data to compare * @return True if data matches given string */ public static boolean streq(byte[] data, String str) { if (data == null) { return false; } return new String(data, ZMQ.CHARSET).compareTo(str) == 0; } public boolean equals(byte[] that) { return Arrays.equals(data, that); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (getClass() != other.getClass()) { return false; } ZData that = (ZData) other; return Arrays.equals(this.data, that.data); } @Override public int hashCode() { return Arrays.hashCode(data); } /** * Returns a human - readable representation of data * @return * A text string or hex-encoded string if data contains any non-printable ASCII characters */ public String toString() { return toString(data); } public static String toString(byte[] data) { if (data == null) { return ""; } // Dump message as text or hex-encoded string boolean isText = true; for (byte aData : data) { if (aData < 32 || aData > 127) { isText = false; break; } } if (isText) { return new String(data, ZMQ.CHARSET); } else { return strhex(data); } } /** * @return data as a printable hex string */ public String strhex() { return strhex(data); } public static String strhex(byte[] data) { if (data == null) { return ""; } StringBuilder b = new StringBuilder(data.length * 2); for (byte aData : data) { int b1 = aData >>> 4 & 0xf; int b2 = aData & 0xf; b.append(HEX_CHAR.charAt(b1)); b.append(HEX_CHAR.charAt(b2)); } return b.toString(); } public void print(PrintStream out, String prefix) { print(out, prefix, data, data.length); } public static void print(PrintStream out, String prefix, byte[] data, int size) { if (data == null) { return; } if (prefix != null) { out.printf("%s", prefix); } boolean isBin = false; int charNbr; for (charNbr = 0; charNbr < size; charNbr++) { if (data[charNbr] < 9 || data[charNbr] > 127) { isBin = true; } } out.printf("[%03d] ", size); int maxSize = isBin ? 35 : 70; String elipsis = ""; if (size > maxSize) { size = maxSize; elipsis = "..."; } for (charNbr = 0; charNbr < size; charNbr++) { if (isBin) { out.printf("%02X", data[charNbr]); } else { out.printf("%c", data[charNbr]); } } out.printf("%s\n", elipsis); out.flush(); } } jeromq-0.6.0/src/main/java/org/zeromq/util/ZDigest.java000066400000000000000000000030551455771126300227610ustar00rootroot00000000000000package org.zeromq.util; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * The ZDigest class generates a hash from chunks of data. The current algorithm is SHA-1, chosen for speed. */ public class ZDigest { private final byte[] buffer; private final MessageDigest sha1; /** * Creates a new digester. */ public ZDigest() { this(new byte[8192]); } /** * Creates a new digester. * @param buffer the temp buffer used for computation of streams. */ public ZDigest(byte[] buffer) { this.buffer = buffer; try { sha1 = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public ZDigest update(InputStream input) throws IOException { int read = input.read(buffer); while (read != -1) { sha1.update(buffer, 0, read); read = input.read(buffer); } return this; } public ZDigest update(byte[] input) { return update(input, 0, input.length); } public ZDigest update(byte[] input, int offset, int length) { sha1.update(input, offset, length); return this; } public byte[] data() { return sha1.digest(); } public int size() { return sha1.digest().length; } public String string() { return ZData.toString(data()); } } jeromq-0.6.0/src/main/java/org/zeromq/util/ZMetadata.java000066400000000000000000000136521455771126300232660ustar00rootroot00000000000000package org.zeromq.util; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.util.Collection; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import org.zeromq.ZConfig; import org.zeromq.ZMQ; import zmq.io.Metadata; public class ZMetadata { private final Metadata metadata; public ZMetadata() { this(new Metadata()); } public ZMetadata(Metadata metadata) { this.metadata = metadata; } public Set keySet() { return metadata.keySet(); } public String get(String key) { return metadata.get(key); } public void set(String key, String value) { metadata.put(key, value); } public void remove(String key) { metadata.remove(key); } public byte[] bytes() { return metadata.bytes(); } public String toString() { return metadata.toString(); } public int hashCode() { return metadata.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ZMetadata zMetadata = (ZMetadata) o; return Objects.equals(metadata, zMetadata.metadata); } /** * Returns a {@link Set} view of the properties contained in this metadata. * The set is backed by the metadata, so changes to the metadata are * reflected in the set, and vice-versa. If the metadata is modified * while an iteration over the set is in progress (except through * the iterator's own {@code remove} operation, or through the * {@code setValue} operation on a metadata property returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the metadata, via the {@code Iterator.remove}, * {@code Set.remove}, {@code removeAll}, {@code retainAll} and * {@code clear} operations. It does not support the * {@code add} or {@code addAll} operations. * * @return a set view of the properties contained in this metadata */ public Set> entrySet() { return metadata.entrySet(); } /** * Returns a {@link Collection} view of the values contained in this metadata. * The collection is backed by the metadata, so changes to the map are * reflected in the collection, and vice-versa. If the metadata is * modified while an iteration over the collection is in progress * (except through the iterator's own {@code remove} operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * property from the metadata, via the {@code Iterator.remove}, * {@code Collection.remove}, {@code removeAll}, * {@code retainAll} and {@code clear} operations. It does not * support the {@code add} or {@code addAll} operations. * * @return a collection view of the values contained in this map */ public Collection values() { return metadata.values(); } public void set(Metadata zapProperties) { metadata.set(zapProperties); } /** * Returns {@code true} if this map contains no key-value mappings. * * @return {@code true} if this map contains no key-value mappings */ public boolean isEmpty() { return metadata.isEmpty(); } /** * Returns {@code true} if this metada contains the property requested * * @param property property the name of the property to be tested. * @return {@code true} if this metada contains the property */ public boolean containsKey(String property) { return metadata.containsKey(property); } /** * Removes all the properties. * The map will be empty after this call returns. */ public void clear() { metadata.clear(); } /** * Returns the number of properties. If it contains more properties * than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. * * @return the number of properties */ public int size() { return metadata.size(); } /** * Serialize metadata to an output stream, using the specifications of the ZMTP protocol *
     * property = name value
     * name = OCTET 1*255name-char
     * name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
     * value = 4OCTET *OCTET       ; Size in network byte order
     * 
* * @param stream the output stream * @throws IOException if an I/O error occurs. * @throws IllegalStateException if one of the properties name size is bigger than 255 */ public void write(OutputStream stream) throws IOException { metadata.write(stream); } public static ZMetadata read(String meta) { if (meta == null || meta.isEmpty()) { return null; } try { ByteBuffer buffer = ZMQ.CHARSET.newEncoder().encode(CharBuffer.wrap(meta)); Metadata data = new Metadata(); data.read(buffer, 0, null); return new ZMetadata(data); } catch (CharacterCodingException e) { throw new IllegalArgumentException("Not a parsable metadata string"); } } public static ZMetadata read(ZConfig conf) { ZConfig meta = conf.getChild("metadata"); if (meta == null) { return null; } ZMetadata metadata = new ZMetadata(); for (Entry entry : meta.getValues().entrySet()) { metadata.set(entry.getKey(), entry.getValue()); } return metadata; } } jeromq-0.6.0/src/main/java/org/zeromq/util/package-info.java000066400000000000000000000010471455771126300237330ustar00rootroot00000000000000/** *

Provides high-level utility bindings for ØMQ.

* *

This is the java equivalent of CZMQ project, * but packaged separately to avoid proliferation of classes under the same namespace.

* *

Classes of this package tend to achieve these goals:

*
    *
  • To wrap the ØMQ core API in semantics that lead to shorter, more readable applications.
  • *
  • To provide a space for development of more sophisticated API semantics.
  • *
* */ package org.zeromq.util; jeromq-0.6.0/src/main/java/zmq/000077500000000000000000000000001455771126300162705ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/Command.java000066400000000000000000000056601455771126300205200ustar00rootroot00000000000000package zmq; // This structure defines the commands that can be sent between threads. public class Command { // Object to process the command. final ZObject destination; final Type type; final Object arg; public enum Type { // Sent to I/O thread to let it know that it should // terminate itself. STOP, // Sent to I/O object to make it register with its I/O thread PLUG, // Sent to socket to let it know about the newly created object. OWN, // Attach the engine to the session. If engine is NULL, it informs // session that the connection have failed. ATTACH, // Sent from session to socket to establish pipe(s) between them. // Caller have used inc_seqnum beforehand sending the command. BIND, // Sent by pipe writer to inform dormant pipe reader that there // are messages in the pipe. ACTIVATE_READ, // Sent by pipe reader to inform pipe writer about how many // messages it has read so far. ACTIVATE_WRITE, // Sent by pipe reader to writer after creating a new inpipe. // The parameter is actually of type pipe_t::upipe_t, however, // its definition is private so we'll have to do with void*. HICCUP, // Sent by pipe reader to pipe writer to ask it to terminate // its end of the pipe. PIPE_TERM, // Pipe writer acknowledges pipe_term command. PIPE_TERM_ACK, // Sent by I/O object ot the socket to request the shutdown of // the I/O object. TERM_REQ, // Sent by socket to I/O object to start its shutdown. TERM, // Sent by I/O object to the socket to acknowledge it has // shut down. TERM_ACK, // Transfers the ownership of the closed socket // to the reaper thread. REAP, // Sent by non-reaper socket to reaper to check destroy. REAP_ACK, // Closed socket notifies the reaper that it's already deallocated. REAPED, // TODO V4 provide a description for Command#INPROC_CONNECTED INPROC_CONNECTED, // Sent by reaper thread to the term thread when all the sockets // are successfully deallocated. DONE, // Cancel a single pending I/O call CANCEL } Command(ZObject destination, Type type) { this(destination, type, null); } Command(ZObject destination, Type type, Object arg) { this.destination = destination; this.type = type; this.arg = arg; } public final void process() { destination.processCommand(this); } @Override public String toString() { return "Cmd" + "[" + destination + ", " + (destination == null ? "Reaper" : destination.getTid() + ", ") + type + (arg == null ? "" : ", " + arg) + "]"; } } jeromq-0.6.0/src/main/java/zmq/Config.java000066400000000000000000000060441455771126300203440ustar00rootroot00000000000000package zmq; public enum Config { // Number of new messages in message pipe needed to trigger new memory // allocation. Setting this parameter to 256 decreases the impact of // memory allocation by approximately 99.6% MESSAGE_PIPE_GRANULARITY(256), // Commands in pipe per allocation event. COMMAND_PIPE_GRANULARITY(16), // Determines how often does socket poll for new commands when it // still has unprocessed messages to handle. Thus, if it is set to 100, // socket will process 100 inbound messages before doing the poll. // If there are no unprocessed messages available, poll is done // immediately. Decreasing the value trades overall latency for more // real-time behaviour (less latency peaks). INBOUND_POLL_RATE(100), // Maximal batching size for engines with receiving functionality. // So, if there are 10 messages that fit into the batch size, all of // them may be read by a single 'recv' system call, thus avoiding // unnecessary network stack traversals. IN_BATCH_SIZE(8192), // Maximal batching size for engines with sending functionality. // So, if there are 10 messages that fit into the batch size, all of // them may be written by a single 'send' system call, thus avoiding // unnecessary network stack traversals. OUT_BATCH_SIZE(8192), // Maximal delta between high and low watermark. MAX_WM_DELTA(1024), // Maximum number of events the I/O thread can process in one go. MAX_IO_EVENTS(256), // Maximal delay to process command in API thread (in CPU ticks). // 3,000,000 ticks equals to 1 - 2 milliseconds on current CPUs. // Note that delay is only applied when there is continuous stream of // messages to process. If not so, commands are processed immediately. MAX_COMMAND_DELAY(3000000), // Low-precision clock precision in CPU ticks. 1ms. Value of 1000000 // should be OK for CPU frequencies above 1GHz. If should work // reasonably well for CPU frequencies above 500MHz. For lower CPU // frequencies you may consider lowering this value to get best // possible latencies. CLOCK_PRECISION(1000000), // Maximum transport data unit size for PGM (TPDU). PGM_MAX_TPDU(1500), // On some OSes the signaler has to be emulated using a TCP // connection. In such cases following port is used. // If 0, it lets the OS choose a free port without requiring use of a // global mutex. The original implementation of a Windows signaler // socket used port 5905 instead of letting the OS choose a free port. // https://github.com/zeromq/libzmq/issues/1542 SIGNALER_PORT(0), // Threshold of message size for choosing if message data should // be allocated on heap or on direct memory. // This has a direct impact on transmission of large messages MSG_ALLOCATION_HEAP_THRESHOLD(1024 * 1024); private final int value; Config(int value) { this.value = value; } public int getValue() { return value; } } jeromq-0.6.0/src/main/java/zmq/Ctx.java000066400000000000000000000722601455771126300177000ustar00rootroot00000000000000package zmq; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.nio.channels.SelectableChannel; import java.nio.channels.Selector; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.zeromq.ZMQException; import zmq.io.IOThread; import zmq.pipe.Pipe; import zmq.socket.Sockets; import zmq.util.Errno; import zmq.util.MultiMap; import zmq.util.function.BiFunction; /** * Context object encapsulates all the global state associated with * the library.
* It creates a reaper thread and some IO threads as defined by {@link ZMQ#ZMQ_IO_THREADS}. The thread are created * using a thread factory that defined the UncaughtExceptionHandler as defined by {@link Ctx#setUncaughtExceptionHandler(UncaughtExceptionHandler)} * and defined the thread as a daemon. If a custom thread factory is defined with {@link Ctx#setThreadFactory(BiFunction)}, * all that steps must be handled manually. */ public class Ctx { private static final int WAIT_FOREVER = -1; // Information associated with inproc endpoint. Note that endpoint options // are registered as well so that the peer can access them without a need // for synchronization, handshaking or similar. public static class Endpoint { public final SocketBase socket; public final Options options; public Endpoint(SocketBase socket, Options options) { this.socket = socket; this.options = options; } } private static class PendingConnection { private final Endpoint endpoint; private final Pipe connectPipe; private final Pipe bindPipe; public PendingConnection(Endpoint endpoint, Pipe connectPipe, Pipe bindPipe) { super(); this.endpoint = endpoint; this.connectPipe = connectPipe; this.bindPipe = bindPipe; } } private enum Side { CONNECT, BIND } // Used to check whether the object is a context. private boolean active; // Sockets belonging to this context. We need the list so that // we can notify the sockets when zmq_term() is called. The sockets // will return ETERM then. private final List sockets; // List of unused thread slots. private final Deque emptySlots; // If true, init has been called but no socket has been created // yet. Launching of I/O threads is delayed. private final AtomicBoolean starting = new AtomicBoolean(true); // If true, zmq_term was already called. private volatile boolean terminating; // Synchronization of accesses to global slot-related data: // sockets, emptySlots, terminating. It also synchronizes // access to zombie sockets as such (as opposed to slots) and provides // a memory barrier to ensure that all CPU cores see the same data. private final Lock slotSync; // A list of poll selectors opened under this context. When the context is // destroyed, each of the selectors is closed to ensure resource // deallocation. private final List selectors = new ArrayList<>(); // The reaper thread. private Reaper reaper; // I/O threads. private final List ioThreads; // Array of pointers to mailboxes for both application and I/O threads. private int slotCount; private IMailbox[] slots; // Mailbox for zmq_term thread. private final Mailbox termMailbox; // List of inproc endpoints within this context. private final Map endpoints; // Synchronization of access to the list of inproc endpoints. private final Lock endpointsSync; // Maximum socket ID. private static final AtomicInteger maxSocketId = new AtomicInteger(0); // Maximum number of sockets that can be opened at the same time. private int maxSockets; // Number of I/O threads to launch. private int ioThreadCount; // The thread factory used by the poller private BiFunction threadFactory; // Does context wait (possibly forever) on termination? private boolean blocky; // Synchronization of access to context options. private final Lock optSync; // Synchronization of access to selectors. private final Lock selectorSync = new ReentrantLock(); static final int TERM_TID = 0; private static final int REAPER_TID = 1; private final MultiMap pendingConnections = new MultiMap<>(); private boolean ipv6; private final Errno errno = new Errno(); // Exception handlers to receive notifications of critical exceptions in zmq.poll.Poller and handle uncaught exceptions private UncaughtExceptionHandler exhandler = Thread.getDefaultUncaughtExceptionHandler(); // Exception handlers to receive notifications of exception in zmq.poll.Poller, and can be used for logging private UncaughtExceptionHandler exnotification = (t, e) -> e.printStackTrace(); /** * A class that holds the informations needed to forward channel in monitor sockets. * Of course, it only works with inproc sockets. *

* It uses WeakReference to avoid holding references to channel if the monitor event is * lost. *

* A class is used as a lock in lazy allocation of the needed objects. */ private static class ChannelForwardHolder { private final AtomicInteger handleSource = new AtomicInteger(0); private final Map> map = new ConcurrentHashMap<>(); // The WeakReference is empty when the reference is empty, so keep a reverse empty to clean the direct map. private final Map, Integer> reversemap = new ConcurrentHashMap<>(); private final ReferenceQueue queue = new ReferenceQueue<>(); } private ChannelForwardHolder forwardHolder = null; public Ctx() { active = true; terminating = false; reaper = null; slotCount = 0; slots = null; maxSockets = ZMQ.ZMQ_MAX_SOCKETS_DFLT; ioThreadCount = ZMQ.ZMQ_IO_THREADS_DFLT; threadFactory = this::createThread; ipv6 = false; blocky = true; slotSync = new ReentrantLock(); endpointsSync = new ReentrantLock(); optSync = new ReentrantLock(); termMailbox = new Mailbox(this, "terminater", -1); emptySlots = new ArrayDeque<>(); ioThreads = new ArrayList<>(); sockets = new ArrayList<>(); endpoints = new HashMap<>(); } private void destroy() throws IOException { assert (sockets.isEmpty()); for (IOThread it : ioThreads) { it.stop(); } for (IOThread it : ioThreads) { it.close(); } ioThreads.clear(); selectorSync.lock(); try { for (Selector selector : selectors) { if (selector != null) { selector.close(); } } selectors.clear(); } finally { selectorSync.unlock(); } // Deallocate the reaper thread object. if (reaper != null) { reaper.close(); } // Deallocate the array of mailboxes. No special work is // needed as mailboxes themselves were deallocated with their // corresponding io_thread/socket objects. termMailbox.close(); active = false; } /** * @return false if {@link #terminate()}terminate() has been called. */ public boolean isActive() { return active; } /** * @return false if {@link #terminate()}terminate() has been called. * @deprecated use {@link #isActive()} instead */ @Deprecated public boolean checkTag() { return active; } // This function is called when user invokes zmq_term. If there are // no more sockets open it'll cause all the infrastructure to be shut // down. If there are open sockets still, the deallocation happens // after the last one is closed. public void terminate() { slotSync.lock(); try { // Connect up any pending inproc connections, otherwise we will hang for (Entry pending : pendingConnections.entries()) { SocketBase s = createSocket(ZMQ.ZMQ_PAIR); // create_socket might fail eg: out of memory/sockets limit reached assert (s != null); s.bind(pending.getValue()); s.close(); } if (!starting.get()) { // Check whether termination was already underway, but interrupted and now // restarted. boolean restarted = terminating; terminating = true; // First attempt to terminate the context. if (!restarted) { // First send stop command to sockets so that any blocking calls // can be interrupted. If there are no sockets we can ask reaper // thread to stop. for (SocketBase socket : sockets) { socket.stop(); } if (sockets.isEmpty()) { reaper.stop(); } } } } finally { slotSync.unlock(); } if (!starting.get()) { // Wait till reaper thread closes all the sockets. Command cmd = termMailbox.recv(WAIT_FOREVER); if (cmd == null) { throw new ZMQException(errno.get()); } assert (cmd.type == Command.Type.DONE) : cmd; slotSync.lock(); try { assert (sockets.isEmpty()); } finally { slotSync.unlock(); } } // Deallocate the resources. try { destroy(); } catch (IOException e) { throw new ZError.IOException(e); } } final void shutdown() { slotSync.lock(); try { if (!starting.get() && !terminating) { terminating = true; // Send stop command to sockets so that any blocking calls // can be interrupted. If there are no sockets we can ask reaper // thread to stop. for (SocketBase socket : sockets) { socket.stop(); } if (sockets.isEmpty()) { reaper.stop(); } } } finally { slotSync.unlock(); } } private void chechStarted() { if (!starting.get()) { throw new IllegalStateException("Already started"); } } /** * Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
* It defaults to the value of {@link Thread#getDefaultUncaughtExceptionHandler()} * @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no * explicit handler and will use the one defined for the {@link ThreadGroup}. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { chechStarted(); exhandler = handler; } /** * @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception. */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return exhandler; } /** * In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can * be logged.

* Default to {@link Throwable#printStackTrace()} * @param handler The object to use as this thread's handler for recoverable exceptions notifications. * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setNotificationExceptionHandler(UncaughtExceptionHandler handler) { chechStarted(); exnotification = handler; } /** * @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run() */ public UncaughtExceptionHandler getNotificationExceptionHandler() { return exnotification; } /** * Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for * performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler * will not be changed, so the factory can also be used to set it. * * @param threadFactory the thread factory used by {@link zmq.poll.Poller} * @throws IllegalStateException If context was already initialized by the creation of a socket */ public void setThreadFactory(BiFunction threadFactory) { chechStarted(); this.threadFactory = threadFactory; } /** * @return the current thread factory */ public BiFunction getThreadFactory() { return threadFactory; } /** * Set an option * @param option the option to set * @param optval the option value * @return true is the option is allowed for a context and the value is valid for the option * @throws IllegalStateException If context was already initialized by the creation of a socket, and the * option can't be changed. */ public boolean set(int option, int optval) { if (option == ZMQ.ZMQ_MAX_SOCKETS && optval >= 1) { chechStarted(); optSync.lock(); try { maxSockets = optval; } finally { optSync.unlock(); } } else if (option == ZMQ.ZMQ_IO_THREADS && optval >= 0) { chechStarted(); optSync.lock(); try { ioThreadCount = optval; } finally { optSync.unlock(); } } else if (option == ZMQ.ZMQ_BLOCKY && optval >= 0) { optSync.lock(); try { blocky = (optval != 0); } finally { optSync.unlock(); } } else if (option == ZMQ.ZMQ_IPV6 && optval >= 0) { optSync.lock(); try { ipv6 = (optval != 0); } finally { optSync.unlock(); } } else { return false; } return true; } public int get(int option) { int rc; if (option == ZMQ.ZMQ_MAX_SOCKETS) { rc = maxSockets; } else if (option == ZMQ.ZMQ_IO_THREADS) { rc = ioThreadCount; } else if (option == ZMQ.ZMQ_BLOCKY) { rc = blocky ? 1 : 0; } else if (option == ZMQ.ZMQ_IPV6) { rc = ipv6 ? 1 : 0; } else { throw new IllegalArgumentException("option = " + option); } return rc; } public SocketBase createSocket(int type) { SocketBase s; slotSync.lock(); try { if (starting.compareAndSet(true, false)) { initSlots(); } // Once zmq_term() was called, we can't create new sockets. if (terminating) { throw new ZError.CtxTerminatedException(); } // If maxSockets limit was reached, return error. if (emptySlots.isEmpty()) { throw new ZMQException(ZError.EMFILE); } // Choose a slot for the socket. int slot = emptySlots.pollLast(); // Generate new unique socket ID. int sid = maxSocketId.incrementAndGet(); // Create the socket and register its mailbox. s = Sockets.create(type, this, slot, sid); if (s == null) { emptySlots.addLast(slot); return null; } sockets.add(s); slots[slot] = s.getMailbox(); } finally { slotSync.unlock(); } return s; } private void initSlots() { slotSync.lock(); try { // Initialize the array of mailboxes. Additional two slots are for // zmq_term thread and reaper thread. int ios; optSync.lock(); try { ios = ioThreadCount; slotCount = maxSockets + ioThreadCount + 2; } finally { optSync.unlock(); } slots = new IMailbox[slotCount]; // Initialize the infrastructure for zmq_term thread. slots[TERM_TID] = termMailbox; // Create the reaper thread. reaper = new Reaper(this, REAPER_TID); slots[REAPER_TID] = reaper.getMailbox(); reaper.start(); // Create I/O thread objects and launch them. for (int i = 2; i != ios + 2; i++) { IOThread ioThread = new IOThread(this, i); //alloc_assert (io_thread); ioThreads.add(ioThread); slots[i] = ioThread.getMailbox(); ioThread.start(); } // In the unused part of the slot array, create a list of empty slots. for (int i = slotCount - 1; i >= ios + 2; i--) { emptySlots.add(i); slots[i] = null; } } finally { slotSync.unlock(); } } void destroySocket(SocketBase socket) { slotSync.lock(); // Free the associated thread slot. try { int tid = socket.getTid(); emptySlots.add(tid); slots[tid] = null; // Remove the socket from the list of sockets. sockets.remove(socket); // If zmq_term() was already called and there are no more socket // we can ask reaper thread to terminate. if (terminating && sockets.isEmpty()) { reaper.stop(); } } finally { slotSync.unlock(); } } // Creates a Selector that will be closed when the context is destroyed. public Selector createSelector() { selectorSync.lock(); try { Selector selector = Selector.open(); assert (selector != null); selectors.add(selector); return selector; } catch (IOException e) { throw new ZError.IOException(e); } finally { selectorSync.unlock(); } } public boolean closeSelector(Selector selector) { selectorSync.lock(); try { boolean rc = selectors.remove(selector); if (rc) { try { selector.close(); } catch (IOException e) { throw new ZError.IOException(e); } } return rc; } finally { selectorSync.unlock(); } } // Returns reaper thread object. ZObject getReaper() { return reaper; } // Send command to the destination thread. void sendCommand(int tid, final Command command) { // System.out.println(Thread.currentThread().getName() + ": Sending command " + command); slots[tid].send(command); } // Returns the I/O thread that is the least busy at the moment. // Affinity specifies which I/O threads are eligible (0 = all). // Returns NULL if no I/O thread is available. IOThread chooseIoThread(long affinity) { if (ioThreads.isEmpty()) { return null; } // Find the I/O thread with minimum load. int minLoad = -1; IOThread selectedIoThread = null; for (int i = 0; i != ioThreads.size(); i++) { if (affinity == 0 || (affinity & (1L << i)) > 0) { int load = ioThreads.get(i).getLoad(); if (selectedIoThread == null || load < minLoad) { minLoad = load; selectedIoThread = ioThreads.get(i); } } } return selectedIoThread; } // Management of inproc endpoints. boolean registerEndpoint(String addr, Endpoint endpoint) { endpointsSync.lock(); Endpoint inserted; try { inserted = endpoints.put(addr, endpoint); } finally { endpointsSync.unlock(); } return inserted == null; } boolean unregisterEndpoint(String addr, SocketBase socket) { endpointsSync.lock(); try { Endpoint endpoint = endpoints.get(addr); if (endpoint != null && socket == endpoint.socket) { endpoints.remove(addr); return true; } } finally { endpointsSync.unlock(); } return false; } void unregisterEndpoints(SocketBase socket) { endpointsSync.lock(); try { endpoints.entrySet().removeIf(e -> e.getValue().socket == socket); } finally { endpointsSync.unlock(); } } Endpoint findEndpoint(String addr) { Endpoint endpoint; endpointsSync.lock(); try { endpoint = endpoints.get(addr); if (endpoint == null) { return new Endpoint(null, new Options()); } // Increment the command sequence number of the peer so that it won't // get deallocated until "bind" command is issued by the caller. // The subsequent 'bind' has to be called with inc_seqnum parameter // set to false, so that the seqnum isn't incremented twice. endpoint.socket.incSeqnum(); } finally { endpointsSync.unlock(); } return endpoint; } void pendConnection(String addr, Endpoint endpoint, Pipe[] pipes) { PendingConnection pendingConnection = new PendingConnection(endpoint, pipes[0], pipes[1]); endpointsSync.lock(); try { Endpoint existing = endpoints.get(addr); if (existing == null) { // Still no bind. endpoint.socket.incSeqnum(); pendingConnections.insert(addr, pendingConnection); } else { // Bind has happened in the mean time, connect directly connectInprocSockets(existing.socket, existing.options, pendingConnection, Side.CONNECT); } } finally { endpointsSync.unlock(); } } void connectPending(String addr, SocketBase bindSocket) { endpointsSync.lock(); try { Collection pendings = pendingConnections.remove(addr); if (pendings != null) { for (PendingConnection pending : pendings) { connectInprocSockets(bindSocket, endpoints.get(addr).options, pending, Side.BIND); } } } finally { endpointsSync.unlock(); } } private void connectInprocSockets(SocketBase bindSocket, Options bindOptions, PendingConnection pendingConnection, Side side) { bindSocket.incSeqnum(); pendingConnection.bindPipe.setTid(bindSocket.getTid()); if (!bindOptions.recvIdentity) { Msg msg = pendingConnection.bindPipe.read(); assert (msg != null); } int sndhwm = 0; if (pendingConnection.endpoint.options.sendHwm != 0 && bindOptions.recvHwm != 0) { sndhwm = pendingConnection.endpoint.options.sendHwm + bindOptions.recvHwm; } int rcvhwm = 0; if (pendingConnection.endpoint.options.recvHwm != 0 && bindOptions.sendHwm != 0) { rcvhwm = pendingConnection.endpoint.options.recvHwm + bindOptions.sendHwm; } boolean conflate = pendingConnection.endpoint.options.conflate && (pendingConnection.endpoint.options.type == ZMQ.ZMQ_DEALER || pendingConnection.endpoint.options.type == ZMQ.ZMQ_PULL || pendingConnection.endpoint.options.type == ZMQ.ZMQ_PUSH || pendingConnection.endpoint.options.type == ZMQ.ZMQ_PUB || pendingConnection.endpoint.options.type == ZMQ.ZMQ_SUB); int[] hwms = { conflate ? -1 : sndhwm, conflate ? -1 : rcvhwm }; pendingConnection.connectPipe.setHwms(hwms[1], hwms[0]); pendingConnection.bindPipe.setHwms(hwms[0], hwms[1]); if (bindOptions.canReceiveDisconnectMsg && bindOptions.disconnectMsg != null) { pendingConnection.connectPipe.setDisconnectMsg(bindOptions.disconnectMsg); } if (side == Side.BIND) { Command cmd = new Command(null, Command.Type.BIND, pendingConnection.bindPipe); bindSocket.processCommand(cmd); bindSocket.sendInprocConnected(pendingConnection.endpoint.socket); } else { pendingConnection.connectPipe.sendBind(bindSocket, pendingConnection.bindPipe, false); } // When a ctx is terminated all pending inproc connection will be // connected, but the socket will already be closed and the pipe will be // in waiting_for_delimiter state, which means no more writes can be done // and the identity write fails and causes an assert. Check if the socket // is open before sending. if (pendingConnection.endpoint.options.recvIdentity && pendingConnection.endpoint.socket.isActive()) { Msg id = new Msg(bindOptions.identitySize); id.put(bindOptions.identity, 0, bindOptions.identitySize); id.setFlags(Msg.IDENTITY); boolean written = pendingConnection.bindPipe.write(id); assert (written); pendingConnection.bindPipe.flush(); } // If set, send the hello msg of the peer to the local socket. if (bindOptions.canSendHelloMsg && bindOptions.helloMsg != null) { boolean written = pendingConnection.bindPipe.write(bindOptions.helloMsg); assert (written); pendingConnection.bindPipe.flush(); } } public Errno errno() { return errno; } /** * Forward a channel in a monitor socket. * @param channel a channel to forward * @return the handle of the channel to be forwarded, used to retrieve it in {@link #getForwardedChannel(Integer)} */ int forwardChannel(SelectableChannel channel) { synchronized (ChannelForwardHolder.class) { if (forwardHolder == null) { forwardHolder = new ChannelForwardHolder(); } } WeakReference ref = new WeakReference<>(channel, forwardHolder.queue); int handle = forwardHolder.handleSource.getAndIncrement(); forwardHolder.map.put(handle, ref); forwardHolder.reversemap.put(ref, handle); cleanForwarded(); return handle; } /** * Retrieve a channel, using the handle returned by {@link #forwardChannel(SelectableChannel)}. As WeakReference are used, if the channel was discarded * and a GC ran, it will not be found and this method will return null. * @param handle * @return */ SelectableChannel getForwardedChannel(Integer handle) { cleanForwarded(); WeakReference ref = forwardHolder.map.remove(handle); if (ref != null) { return ref.get(); } else { return null; } } /** * Clean all empty references */ private void cleanForwarded() { Reference ref; while ((ref = forwardHolder.queue.poll()) != null) { Integer handle = forwardHolder.reversemap.remove(ref); forwardHolder.map.remove(handle); } } private Thread createThread(Runnable target, String name) { Thread t = new Thread(target, name); t.setDaemon(true); t.setUncaughtExceptionHandler(getUncaughtExceptionHandler()); return t; } } jeromq-0.6.0/src/main/java/zmq/IMailbox.java000066400000000000000000000002341455771126300206360ustar00rootroot00000000000000package zmq; import java.io.Closeable; public interface IMailbox extends Closeable { void send(final Command cmd); Command recv(long timeout); } jeromq-0.6.0/src/main/java/zmq/Mailbox.java000066400000000000000000000037071455771126300205350ustar00rootroot00000000000000package zmq; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import zmq.util.Errno; public class Mailbox implements IMailbox { // The pipe to store actual commands. private final Deque cpipe; // Signaler to pass signals from writer thread to reader thread. // kept it although a ConcurrentLinkedDeque, because the signaler channel is used in many places. private final Signaler signaler; // mailbox name, for better debugging private final String name; private final Errno errno; public Mailbox(Ctx ctx, String name, int tid) { this.errno = ctx.errno(); cpipe = new ConcurrentLinkedDeque<>(); signaler = new Signaler(ctx, tid, errno); this.name = name; } public SelectableChannel getFd() { return signaler.getFd(); } @Override public void send(final Command cmd) { cpipe.addLast(cmd); signaler.send(); } @Override public Command recv(long timeout) { Command cmd = cpipe.pollFirst(); while (cmd == null) { // Wait for signal from the command sender. boolean rc = signaler.waitEvent(timeout); if (!rc) { assert (errno.get() == ZError.EAGAIN || errno.get() == ZError.EINTR) : errno.get(); break; } // Receive the signal. signaler.recv(); if (errno.get() == ZError.EINTR) { break; } // Get a command. // Another thread may already fetch the command, so loop on it cmd = cpipe.pollFirst(); } return cmd; } @Override public void close() throws IOException { signaler.close(); } @Override public String toString() { return super.toString() + "[" + name + "]"; } } jeromq-0.6.0/src/main/java/zmq/MailboxSafe.java000066400000000000000000000070561455771126300213350ustar00rootroot00000000000000package zmq; import zmq.pipe.YPipe; import zmq.util.Errno; import java.util.ArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; @Deprecated public class MailboxSafe implements IMailbox { // The pipe to store actual commands. private final YPipe cpipe; // Synchronize access to the mailbox from receivers and senders private final ReentrantLock sync; // Condition variable to pass signals from writer thread to reader thread. private final Condition condition; private final ArrayList signalers; // mailbox name, for better debugging private final String name; private final Errno errno; public MailboxSafe(Ctx ctx, ReentrantLock sync, String name) { this.errno = ctx.errno(); this.cpipe = new YPipe<>(Config.COMMAND_PIPE_GRANULARITY.getValue()); this.sync = sync; this.condition = this.sync.newCondition(); this.signalers = new ArrayList<>(10); this.name = name; // Get the pipe into passive state. That way, if the users starts by // polling on the associated file descriptor it will get woken up when // new command is posted. Command cmd = cpipe.read(); assert (cmd == null); } public void addSignaler(Signaler signaler) { this.signalers.add(signaler); } public void removeSignaler(Signaler signaler) { this.signalers.remove(signaler); } public void clearSignalers() { this.signalers.clear(); } @Override public void send(Command cmd) { sync.lock(); try { cpipe.write(cmd, false); boolean ok = cpipe.flush(); if (!ok) { condition.signalAll(); for (Signaler signaler : signalers) { signaler.send(); } } } finally { sync.unlock(); } } @Override public Command recv(long timeout) { Command cmd; // Try to get the command straight away. cmd = cpipe.read(); if (cmd != null) { return cmd; } // If the timeout is zero, it will be quicker to release the lock, giving other a chance to send a command // and immediately relock it. if (timeout == 0) { sync.unlock(); sync.lock(); } else { try { // Wait for signal from the command sender. if (timeout == -1) { condition.await(); } else { condition.await(timeout, TimeUnit.MILLISECONDS); } } catch (InterruptedException e) { // Restore interrupted state... Thread.currentThread().interrupt(); errno.set(ZError.EINTR); return null; } } // Another thread may already fetch the command cmd = cpipe.read(); if (cmd == null) { errno.set(ZError.EAGAIN); return null; } return cmd; } @Override public void close() { // Work around problem that other threads might still be in our // send() method, by waiting on the mutex before disappearing. sync.lock(); sync.unlock(); } @Override public String toString() { return super.toString() + "[" + name + "]"; } } jeromq-0.6.0/src/main/java/zmq/Msg.java000066400000000000000000000274341455771126300176730ustar00rootroot00000000000000package zmq; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.SocketChannel; import java.util.Arrays; import zmq.io.Metadata; import zmq.util.Utils; import zmq.util.Wire; public class Msg { // dynamic message building used when the size is not known in advance public static final class Builder extends Msg { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); public Builder() { super(); } @Override public int size() { return out.size(); } @Override protected Msg put(int index, byte b) { out.write(b); return this; } @Override public Msg put(byte[] src, int off, int len) { if (src == null) { return this; } out.write(src, off, len); setWriteIndex(getWriteIndex() + len); return this; } @Override public Msg put(ByteBuffer src, int off, int len) { if (src == null) { return this; } for (int idx = off; idx < off + len; ++idx) { out.write(src.get(idx)); } setWriteIndex(getWriteIndex() + len); return this; } @Override public Msg putShortString(String data) { if (data == null) { return this; } int length = data.length(); Utils.checkArgument(length < 256, "String must be strictly smaller than 256 characters"); out.write((byte) length); out.write(data.getBytes(ZMQ.CHARSET), 0, length); setWriteIndex(getWriteIndex() + length + 1); return this; } @Override public void setFlags(int flags) { super.setFlags(flags); } public Msg build() { return new Msg(this, out); } } enum Type { DATA, JOIN, LEAVE, DELIMITER } public static final int MORE = 1; // Followed by more parts public static final int COMMAND = 2; // Command frame (see ZMTP spec) public static final int CREDENTIAL = 32; public static final int IDENTITY = 64; public static final int SHARED = 128; /// The maximum length of a group (Radio/Dish) public static final int MAX_GROUP_LENGTH = 255; private Metadata metadata; private int flags; private Type type; // the file descriptor where this message originated, needs to be 64bit due to alignment private SocketChannel fileDesc; private final int size; private final ByteBuffer buf; // keep track of relative write position private int writeIndex = 0; // keep track of relative read position private int readIndex = 0; private int routingId; private String group; public Msg() { this(0); } public Msg(int capacity) { this.type = Type.DATA; this.flags = 0; this.size = capacity; this.buf = ByteBuffer.wrap(new byte[capacity]).order(ByteOrder.BIG_ENDIAN); } public Msg(byte[] src) { if (src == null) { src = new byte[0]; } this.type = Type.DATA; this.flags = 0; this.size = src.length; this.buf = ByteBuffer.wrap(src).order(ByteOrder.BIG_ENDIAN); } public Msg(final ByteBuffer src) { if (src == null) { throw new IllegalArgumentException("ByteBuffer cannot be null"); } this.type = Type.DATA; this.flags = 0; this.buf = src.duplicate(); this.size = buf.remaining(); } public Msg(final Msg m) { if (m == null) { throw new IllegalArgumentException("Msg cannot be null"); } this.type = m.type; this.flags = m.flags; this.size = m.size; this.buf = m.buf != null ? m.buf.duplicate() : null; } private Msg(Msg src, ByteArrayOutputStream out) { this(ByteBuffer.wrap(out.toByteArray())); this.type = src.type; this.flags = src.flags; } public boolean isIdentity() { return (flags & IDENTITY) == IDENTITY; } public boolean isDelimiter() { return type == Type.DELIMITER; } public boolean isJoin() { return type == Type.JOIN; } public boolean isLeave() { return type == Type.LEAVE; } public boolean check() { return true; // type >= TYPE_MIN && type <= TYPE_MAX; } public int flags() { return flags; } public boolean hasMore() { return (flags & MORE) > 0; } public boolean isCommand() { return (flags & COMMAND) == COMMAND; } public boolean isCredential() { return (flags & CREDENTIAL) == CREDENTIAL; } public void setFlags(int flags) { this.flags |= flags; } public void initDelimiter() { type = Type.DELIMITER; metadata = null; flags = 0; } public void initJoin() { type = Type.JOIN; metadata = null; flags = 0; } public void initLeave() { type = Type.LEAVE; metadata = null; flags = 0; } /** * Returns the message data. *

* If possible, a reference to the data is returned, without copy. * Otherwise a new byte array will be allocated and the data will be copied. * * @return the message data. */ public byte[] data() { if (buf.hasArray()) { byte[] array = buf.array(); int offset = buf.arrayOffset(); if (offset == 0 && array.length == size) { // If the backing array is exactly what we need, return it without copy. return array; } else { // Else use it to make an efficient copy. return Arrays.copyOfRange(array, offset, offset + size); } } else { // No backing array -> use ByteBuffer#get(). byte[] array = new byte[size]; ByteBuffer dup = buf.duplicate(); dup.position(0); dup.get(array); return array; } } public ByteBuffer buf() { return buf.duplicate(); } public int size() { return size; } public void resetFlags(int f) { flags = flags & ~f; } public void setFd(SocketChannel fileDesc) { this.fileDesc = fileDesc; } // TODO V4 use the source channel public SocketChannel fd() { return fileDesc; } public Metadata getMetadata() { return metadata; } public Msg setMetadata(Metadata metadata) { this.metadata = metadata; return this; } public void resetMetadata() { setMetadata(null); } public byte get() { return get(readIndex++); } public byte get(int index) { return buf.get(index); } public Msg put(byte b) { return put(writeIndex++, b); } public Msg put(int b) { return put(writeIndex++, (byte) b); } protected Msg put(int index, byte b) { buf.put(index, b); return this; } public Msg put(byte[] src) { return put(src, 0, src.length); } public Msg put(byte[] src, int off, int len) { if (src == null) { return this; } ByteBuffer dup = buf.duplicate(); dup.position(writeIndex); writeIndex += len; dup.put(src, off, len); return this; } public Msg put(ByteBuffer src, int off, int len) { if (src == null) { return this; } int position = src.position(); int limit = src.limit(); src.limit(off + len).position(off); put(src); src.limit(limit).position(position); return this; } public Msg put(ByteBuffer src) { ByteBuffer dup = buf.duplicate(); dup.position(writeIndex); writeIndex += Math.min(dup.remaining(), src.remaining()); dup.put(src); return this; } public int getBytes(int index, byte[] dst, int off, int len) { int count = Math.min(len, size - index); if (buf.hasArray()) { System.arraycopy(buf.array(), buf.arrayOffset() + index, dst, off, count); } else { ByteBuffer dup = buf.duplicate(); dup.position(index); dup.get(dst, off, count); } return count; } public int getBytes(int index, ByteBuffer bb, int len) { ByteBuffer dup = buf.duplicate(); dup.position(index); int count = Math.min(bb.remaining(), dup.remaining()); count = Math.min(count, len); bb.put(dup); return count; } @Override public String toString() { return String.format("#zmq.Msg{type=%s, size=%s, flags=%s}", type, size, flags); } protected final int getWriteIndex() { return writeIndex; } protected final void setWriteIndex(int writeIndex) { this.writeIndex = writeIndex; } public long getLong(int offset) { return Wire.getUInt64(buf, offset); } public int getInt(int offset) { return Wire.getUInt32(buf, offset); } public int getShort(int offset) { return Wire.getUInt16(buf, offset); } public void transfer(ByteBuffer destination, int srcOffset, int srcLength) { int position = buf.position(); int limit = buf.limit(); buf.limit(srcOffset + srcLength).position(srcOffset); destination.put(buf); buf.limit(limit).position(position); } /** * Puts a string into the message, prefixed with its length. * Users shall size the message by adding 1 to the length of the string: * It needs to be able to accommodate (data.length+1) more bytes. * * @param data a string shorter than 256 characters. If null, defaults to a no-op. * @return the same message. */ public Msg putShortString(String data) { if (data == null) { return this; } ByteBuffer dup = buf.duplicate(); dup.position(writeIndex); writeIndex += Wire.putShortString(dup, data); return this; } /** * Return the routing id of a message. The routing id represent the CLIENT socket that sent the message to the * SERVER socket. * @return the routing id * */ public int getRoutingId() { return routingId; } /** * Set the routing id on a message. The routing id represent the CLIENT socket which the message should be sent to. * Only SERVER socket is currently using the routing id. * @param routingId the routing id * @return true if successfully set the routing id. */ public boolean setRoutingId(int routingId) { if (routingId != 0) { this.routingId = routingId; return true; } return false; } /** * Retrieve the group for RADIO/DISH sockets * @return the group. */ public String getGroup() { return group; } /** * Set the group for RADIO/DISH sockets * @param group * @return true if successfully set the group. */ public boolean setGroup(String group) { if (group.length() > MAX_GROUP_LENGTH) { return false; } this.group = group; return true; } public void resetRoutingId() { routingId = 0; } } jeromq-0.6.0/src/main/java/zmq/Options.java000066400000000000000000000671141455771126300205770ustar00rootroot00000000000000package zmq; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import zmq.io.coder.IDecoder; import zmq.io.coder.IEncoder; import zmq.io.mechanism.Mechanisms; import zmq.io.net.SelectorProviderChooser; import zmq.io.net.ipc.IpcAddress; import zmq.io.net.tcp.TcpAddress; import zmq.io.net.tcp.TcpAddress.TcpAddressMask; import zmq.msg.MsgAllocator; import zmq.msg.MsgAllocatorThreshold; import zmq.util.Errno; import zmq.util.ValueReference; import zmq.util.Z85; public class Options { // High-water marks for message pipes. public int sendHwm = ZMQ.DEFAULT_SEND_HWM; public int recvHwm = ZMQ.DEFAULT_RECV_HWM; // I/O thread affinity. public long affinity = ZMQ.DEFAULT_AFFINITY; // Socket identity public short identitySize = (short) ZMQ.DEFAULT_IDENTITY.length; public byte[] identity = ZMQ.DEFAULT_IDENTITY; // Maximum tranfer rate [kb/s]. Default 100kb/s. int rate = ZMQ.DEFAULT_RATE; // Reliability time interval [ms]. Default 10 seconds. int recoveryIvl = ZMQ.DEFAULT_RECOVERY_IVL; // Sets the time-to-live field in every multicast packet sent. int multicastHops = ZMQ.DEFAULT_MULTICAST_HOPS; // SO_SNDBUF and SO_RCVBUF to be passed to underlying transport sockets. public int sndbuf = ZMQ.DEFAULT_SNDBUF; public int rcvbuf = ZMQ.DEFAULT_RCVBUF; // Type of service (containing DSCP and ECN socket options) public int tos = ZMQ.DEFAULT_TOS; // Socket type. public int type = ZMQ.DEFAULT_TYPE; // Linger time, in milliseconds. public int linger = ZMQ.DEFAULT_LINGER; // Minimum interval between attempts to reconnect, in milliseconds. // Default 100ms public int reconnectIvl = ZMQ.DEFAULT_RECONNECT_IVL; // Maximum interval between attempts to reconnect, in milliseconds. // Default 0 (unused) public int reconnectIvlMax = ZMQ.DEFAULT_RECONNECT_IVL_MAX; // Maximum backlog for pending connections. public int backlog = ZMQ.DEFAULT_BACKLOG; // Maximal size of message to handle. public long maxMsgSize = ZMQ.DEFAULT_MAX_MSG_SIZE; // The timeout for send/recv operations for this socket. int recvTimeout = ZMQ.DEFAULT_RECV_TIMEOUT; int sendTimeout = ZMQ.DEFAULT_SEND_TIMEOUT; // If true, IPv6 is enabled (as well as IPv4) public boolean ipv6 = ZMQ.DEFAULT_IPV6; // If false, connecting pipes are not attached immediately, meaning a send() // on a socket with only connecting pipes would block public boolean immediate = ZMQ.DEFAULT_IMMEDIATE; // Addres of SOCKS proxy public String socksProxyAddress = ZMQ.DEFAULT_SOCKS_PROXY_ADDRESS; // TCP keep-alive settings. // Defaults to -1 = do not change socket options public int tcpKeepAlive = ZMQ.DEFAULT_TCP_KEEP_ALIVE; public int tcpKeepAliveCnt = ZMQ.DEFAULT_TCP_KEEP_ALIVE_CNT; public int tcpKeepAliveIdle = ZMQ.DEFAULT_TCP_KEEP_ALIVE_IDLE; public int tcpKeepAliveIntvl = ZMQ.DEFAULT_TCP_KEEP_ALIVE_INTVL; // Security mechanism for all connections on this socket public Mechanisms mechanism = ZMQ.DEFAULT_MECHANISM; // If peer is acting as server for PLAIN or CURVE mechanisms public boolean asServer = ZMQ.DEFAULT_AS_SERVER; // ZAP authentication domain public String zapDomain = ZMQ.DEFAULT_ZAP_DOMAIN; // Security credentials for PLAIN mechanism public String plainUsername = null; public String plainPassword = null; // Security credentials for CURVE mechanism // Normal base 256 key is 32 bytes public static final int CURVE_KEYSIZE = 32; // Key encoded using Z85 is 40 bytes public static final int CURVE_KEYSIZE_Z85 = 40; // No default, as an array can't really be a static final public byte[] curvePublicKey = new byte[CURVE_KEYSIZE]; public byte[] curveSecretKey = new byte[CURVE_KEYSIZE]; public byte[] curveServerKey = new byte[CURVE_KEYSIZE]; // Principals for GSSAPI mechanism String gssPrincipal = null; String gssServicePrincipal = null; // If true, gss encryption will be disabled boolean gssPlaintext = ZMQ.DEFAULT_GSS_PLAINTEXT; // If true, socket conflates outgoing/incoming messages. // Applicable to dealer, push/pull, pub/sub socket types. // Cannot receive multi-part messages. // Ignores hwm public boolean conflate = ZMQ.DEFAULT_CONFLATE; // If connection handshake is not done after this many milliseconds, // close socket. Default is 30 secs. 0 means no handshake timeout. public int handshakeIvl = ZMQ.DEFAULT_HANDSHAKE_IVL; // If remote peer receives a PING message and doesn't receive another // message within the ttl value, it should close the connection // (measured in tenths of a second) public int heartbeatTtl = ZMQ.DEFAULT_HEARTBEAT_TTL; // Time in milliseconds between sending heartbeat PING messages. public int heartbeatInterval = ZMQ.DEFAULT_HEARTBEAT_INTERVAL; // Time in milliseconds to wait for a PING response before disconnecting public int heartbeatTimeout = ZMQ.DEFAULT_HEARTBEAT_TIMEOUT; // the ping context that will be sent with each ping message. public byte[] heartbeatContext = ZMQ.DEFAULT_HEARTBEAT_CONTEXT; // threshold to allocate byte buffers on direct memory instead of heap. // Set to <= 0 to disable this system. public MsgAllocator allocator = ZMQ.DEFAULT_MSG_ALLOCATOR; public SelectorProviderChooser selectorChooser = ZMQ.DEFAULT_SELECTOR_CHOOSER; // Hello msg to send to peer upon connecting public Msg helloMsg = ZMQ.DEFAULT_HELLO_MSG; public boolean canSendHelloMsg = false; // Disconnect message to receive when peer disconnect public Msg disconnectMsg = ZMQ.DEFAULT_DISCONNECT_MSG; public boolean canReceiveDisconnectMsg = false; // Hiccup message to receive when the connecting peer experience an hiccup in the connection public Msg hiccupMsg = ZMQ.DEFAULT_HICCUP_MSG; public boolean canReceiveHiccupMsg = false; // As Socket type on the network. public int asType = ZMQ.DEFAULT_AS_TYPE; // A metadata record name where the self address will be stored if defined public String selfAddressPropertyName = ZMQ.DEFAULT_SELF_ADDRESS_PROPERTY_NAME; // Last socket endpoint resolved URI String lastEndpoint = null; public final Errno errno = new Errno(); // TCP accept() filters public final List tcpAcceptFilters = new ArrayList<>(); // IPC accept() filters final List ipcAcceptFilters = new ArrayList<>(); // Last connected routing id for PEER socket public int peerLastRoutingId = 0; // ID of the socket. public int socketId = 0; // If 1, (X)SUB socket should filter the messages. If 0, it should not. public boolean filter = false; // If true, the identity message is forwarded to the socket. public boolean recvIdentity = false; // if true, router socket accepts non-zmq tcp connections public boolean rawSocket = false; public Class decoder = null; public Class encoder = null; @SuppressWarnings("deprecation") public boolean setSocketOpt(int option, Object optval) { final ValueReference result = new ValueReference<>(false); switch (option) { case ZMQ.ZMQ_SNDHWM: sendHwm = (Integer) optval; if (sendHwm < 0) { throw new IllegalArgumentException("sendHwm " + optval); } return true; case ZMQ.ZMQ_RCVHWM: recvHwm = (Integer) optval; if (recvHwm < 0) { throw new IllegalArgumentException("recvHwm " + optval); } return true; case ZMQ.ZMQ_AFFINITY: affinity = (Long) optval; return true; case ZMQ.ZMQ_IDENTITY: byte[] val = parseBytes(option, optval); if (val == null || val.length > 255) { throw new IllegalArgumentException("identity must not be null or less than 255 " + optval); } identity = Arrays.copyOf(val, val.length); identitySize = (short) identity.length; return true; case ZMQ.ZMQ_RATE: rate = (Integer) optval; return true; case ZMQ.ZMQ_RECOVERY_IVL: recoveryIvl = (Integer) optval; return true; case ZMQ.ZMQ_SNDBUF: sndbuf = (Integer) optval; return true; case ZMQ.ZMQ_RCVBUF: rcvbuf = (Integer) optval; return true; case ZMQ.ZMQ_TOS: tos = (Integer) optval; return true; case ZMQ.ZMQ_LINGER: linger = (Integer) optval; return true; case ZMQ.ZMQ_RECONNECT_IVL: reconnectIvl = (Integer) optval; if (reconnectIvl < -1) { throw new IllegalArgumentException("reconnectIvl " + optval); } return true; case ZMQ.ZMQ_RECONNECT_IVL_MAX: reconnectIvlMax = (Integer) optval; if (reconnectIvlMax < 0) { throw new IllegalArgumentException("reconnectIvlMax " + optval); } return true; case ZMQ.ZMQ_BACKLOG: backlog = (Integer) optval; return true; case ZMQ.ZMQ_MAXMSGSIZE: maxMsgSize = (Long) optval; return true; case ZMQ.ZMQ_MULTICAST_HOPS: multicastHops = (Integer) optval; return true; case ZMQ.ZMQ_RCVTIMEO: recvTimeout = (Integer) optval; return true; case ZMQ.ZMQ_SNDTIMEO: sendTimeout = (Integer) optval; return true; /* Deprecated in favor of ZMQ_IPV6 */ case ZMQ.ZMQ_IPV4ONLY: return setSocketOpt(ZMQ.ZMQ_IPV6, !parseBoolean(option, optval)); /* To replace the somewhat surprising IPV4ONLY */ case ZMQ.ZMQ_IPV6: ipv6 = parseBoolean(option, optval); return true; case ZMQ.ZMQ_SOCKS_PROXY: socksProxyAddress = parseString(option, optval); return true; case ZMQ.ZMQ_TCP_KEEPALIVE: tcpKeepAlive = ((Number) optval).intValue(); if (tcpKeepAlive != -1 && tcpKeepAlive != 0 && tcpKeepAlive != 1) { throw new IllegalArgumentException("tcpKeepAlive only accepts one of -1,0,1 " + optval); } return true; case ZMQ.ZMQ_TCP_KEEPALIVE_CNT: this.tcpKeepAliveCnt = ((Number) optval).intValue(); return true; case ZMQ.ZMQ_TCP_KEEPALIVE_IDLE: this.tcpKeepAliveIdle = ((Number) optval).intValue(); return true; case ZMQ.ZMQ_TCP_KEEPALIVE_INTVL: this.tcpKeepAliveIntvl = ((Number) optval).intValue(); return true; case ZMQ.ZMQ_IMMEDIATE: immediate = parseBoolean(option, optval); return true; case ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT: immediate = !parseBoolean(option, optval); return true; case ZMQ.ZMQ_TCP_ACCEPT_FILTER: String filterStr = parseString(option, optval); if (filterStr == null) { tcpAcceptFilters.clear(); } else if (filterStr.isEmpty() || filterStr.length() > 255) { throw new IllegalArgumentException("tcp_accept_filter " + optval); } else { TcpAddressMask filter = new TcpAddressMask(filterStr, ipv6); tcpAcceptFilters.add(filter); } return true; case ZMQ.ZMQ_PLAIN_SERVER: asServer = parseBoolean(option, optval); mechanism = (asServer ? Mechanisms.PLAIN : Mechanisms.NULL); return true; case ZMQ.ZMQ_PLAIN_USERNAME: if (optval == null) { mechanism = Mechanisms.NULL; asServer = false; return true; } plainUsername = parseString(option, optval); asServer = false; mechanism = Mechanisms.PLAIN; return true; case ZMQ.ZMQ_PLAIN_PASSWORD: if (optval == null) { mechanism = Mechanisms.NULL; asServer = false; return true; } plainPassword = parseString(option, optval); asServer = false; mechanism = Mechanisms.PLAIN; return true; case ZMQ.ZMQ_ZAP_DOMAIN: String domain = parseString(option, optval); if (domain != null && domain.length() < 256) { zapDomain = domain; return true; } throw new IllegalArgumentException("zap domain length shall be < 256 : " + optval); case ZMQ.ZMQ_CURVE_SERVER: asServer = parseBoolean(option, optval); mechanism = (asServer ? Mechanisms.CURVE : Mechanisms.NULL); return true; case ZMQ.ZMQ_CURVE_PUBLICKEY: curvePublicKey = setCurveKey(option, optval, result); return result.get(); case ZMQ.ZMQ_CURVE_SECRETKEY: curveSecretKey = setCurveKey(option, optval, result); return result.get(); case ZMQ.ZMQ_CURVE_SERVERKEY: curveServerKey = setCurveKey(option, optval, result); if (curveServerKey == null) { asServer = false; } return result.get(); case ZMQ.ZMQ_CONFLATE: conflate = parseBoolean(option, optval); return true; case ZMQ.ZMQ_GSSAPI_SERVER: asServer = parseBoolean(option, optval); mechanism = Mechanisms.GSSAPI; return true; case ZMQ.ZMQ_GSSAPI_PRINCIPAL: gssPrincipal = parseString(option, optval); mechanism = Mechanisms.GSSAPI; return true; case ZMQ.ZMQ_GSSAPI_SERVICE_PRINCIPAL: gssServicePrincipal = parseString(option, optval); mechanism = Mechanisms.GSSAPI; return true; case ZMQ.ZMQ_GSSAPI_PLAINTEXT: gssPlaintext = parseBoolean(option, optval); return true; case ZMQ.ZMQ_HANDSHAKE_IVL: handshakeIvl = (Integer) optval; if (handshakeIvl < 0) { throw new IllegalArgumentException("handshakeIvl only accept positive values " + optval); } return true; case ZMQ.ZMQ_HEARTBEAT_IVL: heartbeatInterval = (Integer) optval; if (heartbeatInterval < 0) { throw new IllegalArgumentException("heartbeatInterval only accept positive values " + optval); } return true; case ZMQ.ZMQ_HEARTBEAT_TIMEOUT: heartbeatTimeout = (Integer) optval; if (heartbeatTimeout < 0) { throw new IllegalArgumentException("heartbeatTimeout only accept positive values " + optval); } return true; case ZMQ.ZMQ_HEARTBEAT_TTL: Integer value = (Integer) optval; // Convert this to deciseconds from milliseconds value /= 100; if (value >= 0 && value <= 6553) { heartbeatTtl = value; } else { throw new IllegalArgumentException("heartbeatTtl is out of range [0..655399]" + optval); } return true; case ZMQ.ZMQ_HEARTBEAT_CONTEXT: heartbeatContext = (byte[]) optval; if (heartbeatContext == null) { throw new IllegalArgumentException("heartbeatContext cannot be null"); } return true; case ZMQ.ZMQ_DECODER: decoder = checkCustomCodec(optval, IDecoder.class); rawSocket = true; // failure throws ZError.InstantiationException // if that line is reached, everything is fine return true; case ZMQ.ZMQ_ENCODER: encoder = checkCustomCodec(optval, IEncoder.class); rawSocket = true; // failure throws ZError.InstantiationException // if that line is reached, everything is fine return true; case ZMQ.ZMQ_MSG_ALLOCATOR: if (optval instanceof String) { try { allocator = allocator(Class.forName((String) optval)); return true; } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } else if (optval instanceof Class) { allocator = allocator((Class) optval); return true; } else if (optval instanceof MsgAllocator) { allocator = (MsgAllocator) optval; return true; } return false; case ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD: Integer allocationHeapThreshold = (Integer) optval; allocator = new MsgAllocatorThreshold(allocationHeapThreshold); return true; case ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER: if (optval instanceof String) { try { selectorChooser = chooser(Class.forName((String) optval)); return true; } catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); } } else if (optval instanceof Class) { selectorChooser = chooser((Class) optval); return true; } else if (optval instanceof SelectorProviderChooser) { selectorChooser = (SelectorProviderChooser) optval; return true; } return false; case ZMQ.ZMQ_HELLO_MSG: if (optval == null) { helloMsg = null; } else { byte[] bytes = parseBytes(option, optval); if (bytes.length == 0) { helloMsg = null; } else { helloMsg = new Msg(Arrays.copyOf(bytes, bytes.length)); } } return true; case ZMQ.ZMQ_DISCONNECT_MSG: if (optval == null) { disconnectMsg = null; } else { byte[] bytes = parseBytes(option, optval); if (bytes.length == 0) { disconnectMsg = null; } else { disconnectMsg = new Msg(Arrays.copyOf(bytes, bytes.length)); } } return true; case ZMQ.ZMQ_HICCUP_MSG: if (optval == null) { hiccupMsg = null; } else { byte[] bytes = parseBytes(option, optval); if (bytes.length == 0) { hiccupMsg = null; } else { hiccupMsg = new Msg(Arrays.copyOf(bytes, bytes.length)); } } return true; case ZMQ.ZMQ_AS_TYPE: this.asType = (Integer) optval; return true; case ZMQ.ZMQ_SELFADDR_PROPERTY_NAME: this.selfAddressPropertyName = parseString(option, optval); return true; default: throw new IllegalArgumentException("Unknown Option " + option); } } private MsgAllocator allocator(Class clazz) { try { Class msgAllocator = clazz.asSubclass(MsgAllocator.class); return msgAllocator.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalArgumentException(e); } } private SelectorProviderChooser chooser(Class clazz) { try { Class selectorClazz = clazz.asSubclass(SelectorProviderChooser.class); return selectorClazz.newInstance(); } catch (ClassCastException e) { throw new IllegalArgumentException(e); } catch (InstantiationException | IllegalAccessException e) { throw new ZError.InstantiationException(e); } } private Class checkCustomCodec(Object optval, Class type) { Class clazz = (Class) optval; if (! type.isAssignableFrom(clazz)) { throw new ZError.InstantiationException("Custom " + clazz.getCanonicalName() + " is not assignable from " + type.getCanonicalName()); } Class custom = clazz.asSubclass(type); try { custom.getConstructor(int.class, long.class); return custom; } catch (NoSuchMethodException | SecurityException e) { String message = "Custom " + clazz.getCanonicalName() + " has no required constructor (int bufferSize, long maxMsgSize)"; throw new ZError.InstantiationException(message, e); } } private byte[] setCurveKey(int option, Object optval, ValueReference result) { if (optval == null) { // TODO V4 setting a curve key as null does change the mechanism type ? result.set(false); return null; } else { byte[] key = null; // if the optval is already the key don't do any parsing if (optval instanceof byte[] && ((byte[]) optval).length == CURVE_KEYSIZE) { key = (byte[]) optval; result.set(true); errno.set(0); } else { String val = parseString(option, optval); int length = val.length(); if (length == CURVE_KEYSIZE_Z85) { key = Z85.decode(val); result.set(true); errno.set(0); } else if (length == CURVE_KEYSIZE) { key = val.getBytes(ZMQ.CHARSET); result.set(true); errno.set(0); } else { result.set(false); errno.set(ZError.EINVAL); } } if (key != null) { mechanism = Mechanisms.CURVE; } return key; } } @SuppressWarnings("deprecation") public Object getSocketOpt(int option) { switch (option) { case ZMQ.ZMQ_SNDHWM: return sendHwm; case ZMQ.ZMQ_RCVHWM: return recvHwm; case ZMQ.ZMQ_AFFINITY: return affinity; case ZMQ.ZMQ_IDENTITY: return identity; case ZMQ.ZMQ_RATE: return rate; case ZMQ.ZMQ_RECOVERY_IVL: return recoveryIvl; case ZMQ.ZMQ_SNDBUF: return sndbuf; case ZMQ.ZMQ_RCVBUF: return rcvbuf; case ZMQ.ZMQ_TOS: return tos; case ZMQ.ZMQ_TYPE: return type; case ZMQ.ZMQ_LINGER: return linger; case ZMQ.ZMQ_RECONNECT_IVL: return reconnectIvl; case ZMQ.ZMQ_RECONNECT_IVL_MAX: return reconnectIvlMax; case ZMQ.ZMQ_BACKLOG: return backlog; case ZMQ.ZMQ_MAXMSGSIZE: return maxMsgSize; case ZMQ.ZMQ_MULTICAST_HOPS: return multicastHops; case ZMQ.ZMQ_RCVTIMEO: return recvTimeout; case ZMQ.ZMQ_SNDTIMEO: return sendTimeout; case ZMQ.ZMQ_IPV4ONLY: return !ipv6; case ZMQ.ZMQ_IPV6: return ipv6; case ZMQ.ZMQ_TCP_KEEPALIVE: return tcpKeepAlive; case ZMQ.ZMQ_IMMEDIATE: return immediate; case ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT: return !immediate; case ZMQ.ZMQ_SOCKS_PROXY: return socksProxyAddress; case ZMQ.ZMQ_TCP_KEEPALIVE_CNT: return tcpKeepAliveCnt; case ZMQ.ZMQ_TCP_KEEPALIVE_IDLE: return tcpKeepAliveIdle; case ZMQ.ZMQ_TCP_KEEPALIVE_INTVL: return tcpKeepAliveIntvl; case ZMQ.ZMQ_MECHANISM: return mechanism; case ZMQ.ZMQ_PLAIN_SERVER: return asServer && mechanism == Mechanisms.PLAIN; case ZMQ.ZMQ_PLAIN_USERNAME: return plainUsername; case ZMQ.ZMQ_PLAIN_PASSWORD: return plainPassword; case ZMQ.ZMQ_ZAP_DOMAIN: return zapDomain; case ZMQ.ZMQ_LAST_ENDPOINT: return lastEndpoint; case ZMQ.ZMQ_CURVE_SERVER: return asServer && mechanism == Mechanisms.CURVE; case ZMQ.ZMQ_CURVE_PUBLICKEY: return curvePublicKey; case ZMQ.ZMQ_CURVE_SERVERKEY: return curveServerKey; case ZMQ.ZMQ_CURVE_SECRETKEY: return curveSecretKey; case ZMQ.ZMQ_CONFLATE: return conflate; case ZMQ.ZMQ_GSSAPI_SERVER: return asServer && mechanism == Mechanisms.GSSAPI; case ZMQ.ZMQ_GSSAPI_PRINCIPAL: return gssPrincipal; case ZMQ.ZMQ_GSSAPI_SERVICE_PRINCIPAL: return gssServicePrincipal; case ZMQ.ZMQ_GSSAPI_PLAINTEXT: return gssPlaintext; case ZMQ.ZMQ_HANDSHAKE_IVL: return handshakeIvl; case ZMQ.ZMQ_HEARTBEAT_IVL: return heartbeatInterval; case ZMQ.ZMQ_HEARTBEAT_TIMEOUT: return heartbeatTimeout; case ZMQ.ZMQ_HEARTBEAT_TTL: // Convert the internal deciseconds value to milliseconds return heartbeatTtl * 100; case ZMQ.ZMQ_HEARTBEAT_CONTEXT: return heartbeatContext; case ZMQ.ZMQ_MSG_ALLOCATOR: return allocator; case ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD: if (allocator instanceof MsgAllocatorThreshold) { MsgAllocatorThreshold all = (MsgAllocatorThreshold) allocator; return all.threshold; } return -1; case ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER: return selectorChooser; case ZMQ.ZMQ_AS_TYPE: return asType; case ZMQ.ZMQ_SELFADDR_PROPERTY_NAME: return selfAddressPropertyName; default: throw new IllegalArgumentException("option=" + option); } } public static boolean parseBoolean(int option, Object optval) { if (optval instanceof Boolean) { return (Boolean) optval; } else if (optval instanceof Integer) { return (Integer) optval != 0; } throw new IllegalArgumentException(optval + " is neither an integer or a boolean for option " + option); } public static String parseString(int option, Object optval) { if (optval instanceof String) { return (String) optval; } else if (optval instanceof byte[]) { return new String((byte[]) optval, ZMQ.CHARSET); } throw new IllegalArgumentException(optval + " is neither a string or an array of bytes for option " + option); } public static byte[] parseBytes(int option, Object optval) { if (optval instanceof String) { return ((String) optval).getBytes(ZMQ.CHARSET); } else if (optval instanceof byte[]) { return (byte[]) optval; } throw new IllegalArgumentException(optval + " is neither a string or an array of bytes for option " + option); } } jeromq-0.6.0/src/main/java/zmq/Own.java000066400000000000000000000166521455771126300177100ustar00rootroot00000000000000package zmq; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import zmq.io.IOThread; import zmq.util.Errno; // Base class for objects forming a part of ownership hierarchy. // It handles initialization and destruction of such objects. public abstract class Own extends ZObject { protected final Options options; // True if termination was already initiated. If so, we can destroy // the object if there are no more child objects or pending term acks. private boolean terminating; // Sequence number of the last command sent to this object. private final AtomicLong sendSeqnum; // Sequence number of the last command processed by this object. private long processedSeqnum; // Socket owning this object. It's responsible for shutting down // this object. private Own owner; // List of all objects owned by this socket. We are responsible // for deallocating them before we quit. //typedef std::set owned_t; private final Set owned; // Number of events we have to get before we can destroy the object. private int termAcks; public final Errno errno; // Note that the owner is unspecified in the constructor. // It'll be supplied later on when the object is plugged in. // The object is not living within an I/O thread. It has it's own // thread outside of 0MQ infrastructure. protected Own(Ctx parent, int tid) { super(parent, tid); terminating = false; sendSeqnum = new AtomicLong(0); processedSeqnum = 0; owner = null; termAcks = 0; options = new Options(); errno = options.errno; owned = new HashSet<>(); } // The object is living within I/O thread. protected Own(IOThread ioThread, Options options) { super(ioThread); this.options = options; terminating = false; sendSeqnum = new AtomicLong(0); processedSeqnum = 0; owner = null; termAcks = 0; errno = options.errno; owned = new HashSet<>(); } protected abstract void destroy(); // A place to hook in when physical destruction of the object // is to be delayed. protected void processDestroy() { destroy(); } private void setOwner(Own owner) { assert (this.owner == null) : this.owner; this.owner = owner; } // When another owned object wants to send command to this object // it calls this function to let it know it should not shut down // before the command is delivered. protected void incSeqnum() { // This function may be called from a different thread! sendSeqnum.incrementAndGet(); } @Override protected final void processSeqnum() { // Catch up with counter of processed commands. processedSeqnum++; // We may have caught up and still have pending terms acks. checkTermAcks(); } // Launch the supplied object and become its owner. protected final void launchChild(Own object) { // Specify the owner of the object. object.setOwner(this); // Plug the object into the I/O thread. sendPlug(object); // Take ownership of the object. sendOwn(this, object); } // Terminate owned object protected final void termChild(Own object) { processTermReq(object); } @Override protected final void processTermReq(Own object) { // When shutting down we can ignore termination requests from owned // objects. The termination request was already sent to the object. if (terminating) { return; } // If I/O object is well and alive let's ask it to terminate. // If not found, we assume that termination request was already sent to // the object so we can safely ignore the request. if (!owned.remove(object)) { return; } registerTermAcks(1); // Note that this object is the root of the (partial shutdown) thus, its // value of linger is used, rather than the value stored by the children. sendTerm(object, options.linger); } @Override protected final void processOwn(Own object) { // If the object is already being shut down, new owned objects are // immediately asked to terminate. Note that linger is set to zero. if (terminating) { registerTermAcks(1); sendTerm(object, 0); return; } // Store the reference to the owned object. owned.add(object); } // Ask owner object to terminate this object. It may take a while // while actual termination is started. This function should not be // called more than once. protected final void terminate() { // If termination is already underway, there's no point // in starting it anew. if (terminating) { return; } // As for the root of the ownership tree, there's no one to terminate it, // so it has to terminate itself. if (owner == null) { processTerm(options.linger); return; } // If I am an owned object, I'll ask my owner to terminate me. sendTermReq(owner, this); } // Returns true if the object is in process of termination. protected final boolean isTerminating() { return terminating; } // Term handler is protected rather than private so that it can // be intercepted by the derived class. This is useful to add custom // steps to the beginning of the termination process. @Override protected void processTerm(int linger) { // Double termination should never happen. assert (!terminating); // Send termination request to all owned objects. for (Own it : owned) { sendTerm(it, linger); } registerTermAcks(owned.size()); owned.clear(); // Start termination process and check whether by chance we cannot // terminate immediately. terminating = true; checkTermAcks(); } // Use following two functions to wait for arbitrary events before // terminating. Just add number of events to wait for using // register_tem_acks functions. When event occurs, call // remove_term_ack. When number of pending acks reaches zero // object will be deallocated. final void registerTermAcks(int count) { termAcks += count; } final void unregisterTermAck() { assert (termAcks > 0); termAcks--; // This may be a last ack we are waiting for before termination... checkTermAcks(); } @Override protected final void processTermAck() { unregisterTermAck(); } private void checkTermAcks() { if (terminating && processedSeqnum == sendSeqnum.get() && termAcks == 0) { // Sanity check. There should be no active children at this point. assert (owned.isEmpty()) : owned; // The root object has nobody to confirm the termination to. // Other nodes will confirm the termination to the owner. if (owner != null) { sendTermAck(owner); } // Deallocate the resources. processDestroy(); } } } jeromq-0.6.0/src/main/java/zmq/Proxy.java000066400000000000000000000131641455771126300202610ustar00rootroot00000000000000package zmq; import java.nio.channels.Selector; import java.util.Arrays; import zmq.poll.PollItem; class Proxy { public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture) { return new Proxy().start(frontend, backend, capture, null); } public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control) { return new Proxy().start(frontend, backend, capture, control); } public enum State { ACTIVE, PAUSED, TERMINATED } private State state; private Proxy() { state = State.ACTIVE; } private boolean start(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control) { // The algorithm below assumes ratio of requests and replies processed // under full load to be 1:1. // TODO: The current implementation drops messages when // any of the pipes becomes full. int rc; int more; Msg msg; int count = control == null ? 2 : 3; PollItem[] items = new PollItem[count]; items[0] = new PollItem(frontend, ZMQ.ZMQ_POLLIN); items[1] = new PollItem(backend, ZMQ.ZMQ_POLLIN); if (control != null) { items[2] = new PollItem(control, ZMQ.ZMQ_POLLIN); } PollItem[] itemsout = new PollItem[2]; itemsout[0] = new PollItem(frontend, ZMQ.ZMQ_POLLOUT); itemsout[1] = new PollItem(backend, ZMQ.ZMQ_POLLOUT); Selector selector = frontend.getCtx().createSelector(); try { while (state != State.TERMINATED) { // Wait while there are either requests or replies to process. rc = ZMQ.poll(selector, items, -1); if (rc < 0) { return false; } // Get the pollout separately because when combining this with pollin it makes the CPU // because pollout shall most of the time return directly. // POLLOUT is only checked when frontend and backend sockets are not the same. if (frontend != backend) { rc = ZMQ.poll(selector, itemsout, 0L); if (rc < 0) { return false; } } // Process a control command if any if (control != null && items[2].isReadable()) { msg = control.recv(0); if (msg == null) { return false; } more = control.getSocketOpt(ZMQ.ZMQ_RCVMORE); if (more < 0) { return false; } // Copy message to capture socket if any boolean success = capture(capture, msg, more); if (!success) { return false; } byte[] command = msg.data(); if (Arrays.equals(command, ZMQ.PROXY_PAUSE)) { state = State.PAUSED; } else if (Arrays.equals(command, ZMQ.PROXY_RESUME)) { state = State.ACTIVE; } else if (Arrays.equals(command, ZMQ.PROXY_TERMINATE)) { state = State.TERMINATED; } else { // This is an API error, we should assert System.out .printf("E: invalid command sent to proxy '%s'%n", new String(command, ZMQ.CHARSET)); assert false; } } // Process a request. if (process(items[0], itemsout[1], frontend, backend)) { if (!forward(frontend, backend, capture)) { return false; } } // Process a reply. if (process(items[1], itemsout[0], frontend, backend)) { if (!forward(backend, frontend, capture)) { return false; } } } } finally { frontend.getCtx().closeSelector(selector); } return true; } private boolean process(PollItem read, PollItem write, SocketBase frontend, SocketBase backend) { return state == State.ACTIVE && read.isReadable() && (frontend == backend || write.isWritable()); } private boolean forward(SocketBase from, SocketBase to, SocketBase capture) { int more; boolean success; while (true) { Msg msg = from.recv(0); if (msg == null) { return false; } more = from.getSocketOpt(ZMQ.ZMQ_RCVMORE); if (more < 0) { return false; } // Copy message to capture socket if any success = capture(capture, msg, more); if (!success) { return false; } success = to.send(msg, more > 0 ? ZMQ.ZMQ_SNDMORE : 0); if (!success) { return false; } if (more == 0) { break; } } return true; } private boolean capture(SocketBase capture, Msg msg, int more) { if (capture != null) { Msg ctrl = new Msg(msg); return capture.send(ctrl, more > 0 ? ZMQ.ZMQ_SNDMORE : 0); } return true; } } jeromq-0.6.0/src/main/java/zmq/Reaper.java000066400000000000000000000051751455771126300203610ustar00rootroot00000000000000package zmq; import java.io.Closeable; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.util.concurrent.atomic.AtomicBoolean; import zmq.poll.IPollEvents; import zmq.poll.Poller; final class Reaper extends ZObject implements IPollEvents, Closeable { // Reaper thread accesses incoming commands via this mailbox. private final Mailbox mailbox; // Handle associated with mailbox' file descriptor. private final Poller.Handle mailboxHandle; // I/O multiplexing is performed using a poller object. private final Poller poller; // Number of sockets being reaped at the moment. private int socketsReaping; // If true, we were already asked to terminate. private final AtomicBoolean terminating = new AtomicBoolean(); Reaper(Ctx ctx, int tid) { super(ctx, tid); socketsReaping = 0; String name = "reaper-" + tid; poller = new Poller(ctx, name); mailbox = new Mailbox(ctx, name, tid); SelectableChannel fd = mailbox.getFd(); mailboxHandle = poller.addHandle(fd, this); poller.setPollIn(mailboxHandle); } @Override public void close() throws IOException { poller.destroy(); mailbox.close(); } Mailbox getMailbox() { return mailbox; } void start() { poller.start(); } void stop() { if (!terminating.get()) { sendStop(); } } @Override public void inEvent() { while (true) { // Get the next command. If there is none, exit. Command cmd = mailbox.recv(0); if (cmd == null) { break; } // Process the command. cmd.process(); } } @Override protected void processStop() { terminating.set(true); // If there are no sockets being reaped finish immediately. if (socketsReaping == 0) { finishTerminating(); } } @Override protected void processReap(SocketBase socket) { ++socketsReaping; // Add the socket to the poller. socket.startReaping(poller); } @Override protected void processReaped() { --socketsReaping; // If reaped was already asked to terminate and there are no more sockets, // finish immediately. if (socketsReaping == 0 && terminating.get()) { finishTerminating(); } } private void finishTerminating() { sendDone(); poller.removeHandle(mailboxHandle); poller.stop(); } } jeromq-0.6.0/src/main/java/zmq/Signaler.java000066400000000000000000000142351455771126300207040ustar00rootroot00000000000000package zmq; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.Pipe; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.concurrent.atomic.AtomicLong; import zmq.util.Errno; import zmq.util.Utils; // This is a cross-platform equivalent to signal_fd. However, as opposed // to signal_fd there can be at most one signal in the signaler at any // given moment. Attempt to send a signal before receiving the previous // one will result in undefined behaviour. final class Signaler implements Closeable { private interface IoOperation { O call() throws IOException; } // Underlying write & read file descriptor. private final Pipe.SinkChannel w; private final Pipe.SourceChannel r; private final Selector selector; private final ThreadLocal wdummy = ThreadLocal.withInitial(() -> ByteBuffer.allocate(1)); private final ThreadLocal rdummy = ThreadLocal.withInitial(() -> ByteBuffer.allocate(1)); // Selector.selectNow at every sending message doesn't show enough performance private final AtomicLong wcursor = new AtomicLong(0); private long rcursor = 0; private final Errno errno; private final int pid; private final Ctx ctx; Signaler(Ctx ctx, int pid, Errno errno) { this.ctx = ctx; this.pid = pid; this.errno = errno; // Create the socket pair for signaling. try { Pipe pipe = Pipe.open(); r = pipe.source(); w = pipe.sink(); // Set both fds to non-blocking mode. Utils.unblockSocket(w, r); selector = ctx.createSelector(); r.register(selector, SelectionKey.OP_READ); } catch (IOException e) { throw new ZError.IOException(e); } } private O maksInterrupt(IoOperation operation) throws IOException { // This loop try to protect the current thread from external interruption. // If it happens, it mangles current context internal state. // So it keep trying until it succeed. // This must only be called on internal IO (using Pipe) boolean interrupted = Thread.interrupted(); while (true) { try { return operation.call(); } catch (ClosedByInterruptException e) { interrupted = true; } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } } @Override public void close() throws IOException { IOException exception = null; IoOperation op1 = () -> { r.close(); return null; }; IoOperation op2 = () -> { w.close(); return null; }; IoOperation op3 = () -> { ctx.closeSelector(selector); return null; }; for (IoOperation op : new IoOperation[] {op1, op2, op3}) { try { maksInterrupt(op); } catch (IOException e) { if (exception != null) { e.addSuppressed(exception); } exception = e; } } if (exception != null) { throw exception; } } SelectableChannel getFd() { return r; } void send() { int nbytes = 0; while (nbytes == 0) { try { wdummy.get().clear(); nbytes = maksInterrupt(() -> w.write(wdummy.get())); } catch (IOException e) { throw new ZError.IOException(e); } } wcursor.incrementAndGet(); } boolean waitEvent(long timeout) { // Transform a interrupt signal in an errno EINTR if (Thread.interrupted()) { errno.set(ZError.EINTR); return false; } int rc; boolean brc = (rcursor < wcursor.get()); if (brc) { return true; } try { if (timeout == 0) { // waitEvent(0) is called every read/send of SocketBase // instant readiness is not strictly required // On the other hand, we can save lots of system call and increase performance errno.set(ZError.EAGAIN); return false; } else if (timeout < 0) { rc = selector.select(0); } else { rc = selector.select(timeout); } } catch (ClosedSelectorException e) { errno.set(ZError.EINTR); return false; } catch (IOException e) { errno.set(ZError.exccode(e)); return false; } if (Thread.interrupted() || (rc == 0 && timeout <= 0 && ! selector.keys().isEmpty())) { errno.set(ZError.EINTR); return false; } else if (rc == 0) { errno.set(ZError.EAGAIN); return false; } selector.selectedKeys().clear(); return true; } void recv() { int nbytes = 0; // On windows, there may be a need to try several times until it succeeds while (nbytes == 0) { try { rdummy.get().clear(); nbytes = maksInterrupt(() -> r.read(rdummy.get())); } catch (ClosedChannelException e) { errno.set(ZError.EINTR); return; } catch (IOException e) { throw new ZError.IOException(e); } } assert (nbytes == 1); rcursor++; } @Override public String toString() { return "Signaler[" + pid + "]"; } } jeromq-0.6.0/src/main/java/zmq/SocketBase.java000066400000000000000000001450501455771126300211630ustar00rootroot00000000000000package zmq; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.io.net.Address.IZAddress; import zmq.io.net.Listener; import zmq.io.net.NetProtocol; import zmq.pipe.Pipe; import zmq.poll.IPollEvents; import zmq.poll.Poller; import zmq.socket.Sockets; import zmq.util.Blob; import zmq.util.Clock; import zmq.util.MultiMap; public abstract class SocketBase extends Own implements IPollEvents, Pipe.IPipeEvents { private static class EndpointPipe { private final Own endpoint; private final Pipe pipe; public EndpointPipe(Own endpoint, Pipe pipe) { super(); this.endpoint = endpoint; this.pipe = pipe; } @Override public String toString() { return "EndpointPipe [endpoint=" + endpoint + ", pipe=" + pipe + "]"; } } /** * The old consumer that forward events through a socket */ private static class SocketEventHandler implements ZMQ.EventConsummer { private final SocketBase monitorSocket; public SocketEventHandler(SocketBase monitorSocket) { this.monitorSocket = monitorSocket; } public void consume(ZMQ.Event ev) { ev.write(monitorSocket); } @Override public void close() { monitorSocket.close(); } } // Map of open endpoints. private final MultiMap endpoints; // Map of open inproc endpoints. private final MultiMap inprocs; // Used to check whether the object is a socket. private boolean active; // If true, associated context was already terminated. private final AtomicBoolean ctxTerminated; // If processCommand function was called from InEvent function. private final ThreadLocal isInEventThreadLocal; // If true, object should have been already destroyed. However, // destruction is delayed while we unwind the stack to the point // where it doesn't intersect the object being destroyed. private final AtomicBoolean destroyed; // Socket's mailbox object. private final IMailbox mailbox; // the attached pipes. private final Set pipes; // Reaper's poller and handle of this socket within it. private Poller poller; private Poller.Handle handle; // Timestamp of when commands were processed the last time. private long lastTsc; // Number of messages received since last command processing. private int ticks; // True if the last message received had MORE flag set. private boolean rcvmore; // File descriptor if applicable private SocketChannel fileDesc; // Bitmask of events being monitored private int monitorEvents; // Next assigned name on a zmq_connect() call used by ROUTER and STREAM socket types protected String connectRid; private final AtomicReference monitor; // Indicate if the socket is thread safe private final boolean threadSafe; // Mutex for synchronize access to the socket in thread safe mode private final ReentrantLock threadSafeSync; protected SocketBase(Ctx parent, int tid, int sid) { this(parent, tid, sid, false); } protected SocketBase(Ctx parent, int tid, int sid, boolean threadSafe) { super(parent, tid); active = true; ctxTerminated = new AtomicBoolean(); isInEventThreadLocal = new ThreadLocal<>(); destroyed = new AtomicBoolean(); lastTsc = 0; ticks = 0; rcvmore = false; monitorEvents = 0; monitor = new AtomicReference<>(null); options.socketId = sid; options.ipv6 = parent.get(ZMQ.ZMQ_IPV6) != 0; options.linger = parent.get(ZMQ.ZMQ_BLOCKY) != 0 ? -1 : 0; endpoints = new MultiMap<>(); inprocs = new MultiMap<>(); pipes = new HashSet<>(); this.threadSafe = threadSafe; this.threadSafeSync = new ReentrantLock(); mailbox = new Mailbox(parent, "socket-" + sid, tid); } // Concrete algorithms for the x- methods are to be defined by // individual socket types. protected abstract void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated); protected abstract void xpipeTerminated(Pipe pipe); /** * @return false if object is not a socket. */ boolean isActive() { return active; } @Override protected void destroy() { synchronized (monitor) { try { mailbox.close(); } catch (IOException ignore) { } stopMonitor(); assert (destroyed.get()); } } // Returns the mailbox associated with this socket. final IMailbox getMailbox() { return mailbox; } // Interrupt blocking call if the socket is stuck in one. // This function can be called from a different thread! final void stop() { // Called by ctx when it is terminated (zmq_term). // 'stop' command is sent from the threads that called zmq_term to // the thread owning the socket. This way, blocking call in the // owner thread can be interrupted. sendStop(); } // Check whether transport protocol, as specified in connect or // bind, is available and compatible with the socket type. private NetProtocol checkProtocol(String protocol) { try { // First check out whether the protcol is something we are aware of. NetProtocol proto = NetProtocol.getProtocol(protocol); if (!proto.valid) { errno.set(ZError.EPROTONOSUPPORT); return proto; } // Check whether socket type and transport protocol match. // Specifically, multicast protocols can't be combined with // bi-directional messaging patterns (socket types). if (!proto.compatible(options.type)) { errno.set(ZError.ENOCOMPATPROTO); return null; } // Protocol is available. return proto; } catch (IllegalArgumentException e) { errno.set(ZError.EPROTONOSUPPORT); return null; } } // Register the pipe with this socket. private void attachPipe(Pipe pipe, boolean isLocallyInitiated) { attachPipe(pipe, false, isLocallyInitiated); } private void attachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); // First, register the pipe so that we can terminate it later on. pipe.setEventSink(this); pipes.add(pipe); // Let the derived socket type know about new pipe. xattachPipe(pipe, subscribe2all, isLocallyInitiated); // If the socket is already being closed, ask any new pipes to terminate // straight away. if (isTerminating()) { registerTermAcks(1); pipe.terminate(false); } } public final boolean setSocketOpt(int option, Object optval) { lock(); try { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } // First, check whether specific socket type overloads the option. boolean rc = xsetsockopt(option, optval); if (rc || errno.get() != ZError.EINVAL) { return rc; } // If the socket type doesn't support the option, pass it to // the generic option parser. rc = options.setSocketOpt(option, optval); if (rc) { errno.set(0); } return rc; } finally { unlock(); } } public final int getSocketOpt(int option) { lock(); try { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return -1; } // fast track to avoid boxing if (option == ZMQ.ZMQ_RCVMORE) { return rcvmore ? 1 : 0; } if (option == ZMQ.ZMQ_EVENTS) { boolean rc = processCommands(0, false, null); if (!rc && (errno.get() == ZError.ETERM || errno.get() == ZError.EINTR)) { return -1; } assert (rc); int val = 0; if (hasOut()) { val |= ZMQ.ZMQ_POLLOUT; } if (hasIn()) { val |= ZMQ.ZMQ_POLLIN; } return val; } Object val = options.getSocketOpt(option); if (val instanceof Integer) { return (Integer) val; } if (val instanceof Boolean) { return (Boolean) val ? 1 : 0; } throw new IllegalArgumentException(val + " is neither an integer or a boolean for option " + option); } finally { unlock(); } } public final Object getSocketOptx(int option) { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return null; } if (option == ZMQ.ZMQ_RCVMORE) { return rcvmore ? 1 : 0; } if (option == ZMQ.ZMQ_FD) { if (threadSafe) { // thread safe socket doesn't provide file descriptor errno.set(ZError.EINVAL); return null; } return ((Mailbox) mailbox).getFd(); } if (option == ZMQ.ZMQ_EVENTS) { boolean rc = processCommands(0, false, null); if (!rc && (errno.get() == ZError.ETERM || errno.get() == ZError.EINTR)) { return -1; } assert (rc); int val = 0; if (hasOut()) { val |= ZMQ.ZMQ_POLLOUT; } if (hasIn()) { val |= ZMQ.ZMQ_POLLIN; } return val; } // If the socket type doesn't support the option, pass it to // the generic option parser. return options.getSocketOpt(option); } public final boolean bind(final String addr) { lock(); try { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } options.mechanism.check(options); // Process pending commands, if any. boolean brc = processCommands(0, false, null); if (!brc) { return false; } SimpleURI uri = SimpleURI.create(addr); String address = uri.getAddress(); NetProtocol protocol = checkProtocol(uri.getProtocol()); if (protocol == null) { return false; } switch (protocol) { case inproc: { Ctx.Endpoint endpoint = new Ctx.Endpoint(this, options); boolean rc = registerEndpoint(addr, endpoint); if (rc) { connectPending(addr, this); // Save last endpoint URI options.lastEndpoint = addr; } else { errno.set(ZError.EADDRINUSE); } return rc; } case pgm: // continue case epgm: // continue case norm: // For convenience's sake, bind can be used interchangeable with // connect for PGM, EPGM and NORM transports. return connect(addr); case tcp: // continue case ipc: // continue case tipc: { // Remaining transports require to be run in an I/O thread, so at this // point we'll choose one. IOThread ioThread = chooseIoThread(options.affinity); if (ioThread == null) { errno.set(ZError.EMTHREAD); return false; } Listener listener = protocol.getListener(ioThread, this, options); boolean rc = listener.setAddress(address); if (!rc) { listener.destroy(); eventBindFailed(address, errno.get()); return false; } // Save last endpoint URI options.lastEndpoint = listener.getAddress(); addEndpoint(options.lastEndpoint, listener, null); return true; } default: throw new IllegalArgumentException(addr); } } finally { unlock(); } } public final boolean connect(String addr) { lock(); try { return connectInternal(addr); } finally { unlock(); } } public final int connectPeer(String addr) { lock(); try { if (options.type != ZMQ.ZMQ_PEER && options.type != ZMQ.ZMQ_RAW) { errno.set(ZError.ENOTSUP); return 0; } boolean rc = connectInternal(addr); if (!rc) { return 0; } return options.peerLastRoutingId; } finally { unlock(); } } private boolean connectInternal(String addr) { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } options.mechanism.check(options); // Process pending commands, if any. boolean brc = processCommands(0, false, null); if (!brc) { return false; } SimpleURI uri = SimpleURI.create(addr); String address = uri.getAddress(); NetProtocol protocol = checkProtocol(uri.getProtocol()); if (protocol == null || !protocol.valid) { return false; } if (protocol == NetProtocol.inproc) { // TODO: inproc connect is specific with respect to creating pipes // as there's no 'reconnect' functionality implemented. Once that // is in place we should follow generic pipe creation algorithm. // Find the peer endpoint. Ctx.Endpoint peer = findEndpoint(addr); // The total HWM for an inproc connection should be the sum of // the binder's HWM and the connector's HWM. int sndhwm = 0; if (peer.socket == null) { sndhwm = options.sendHwm; } else if (options.sendHwm != 0 && peer.options.recvHwm != 0) { sndhwm = options.sendHwm + peer.options.recvHwm; } int rcvhwm = 0; if (peer.socket == null) { rcvhwm = options.recvHwm; } else if (options.recvHwm != 0 && peer.options.sendHwm != 0) { rcvhwm = options.recvHwm + peer.options.sendHwm; } // Create a bi-directional pipe to connect the peers. ZObject[] parents = {this, peer.socket == null ? this : peer.socket}; boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL || options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB); int[] hwms = {conflate ? -1 : sndhwm, conflate ? -1 : rcvhwm}; boolean[] conflates = {conflate, conflate}; Pipe[] pipes = Pipe.pair(parents, hwms, conflates); // Attach local end of the pipe to this socket object. attachPipe(pipes[0], true); if (peer.socket == null) { // The peer doesn't exist yet so we don't know whether // to send the identity message or not. To resolve this, // we always send our identity and drop it later if // the peer doesn't expect it. Msg id = new Msg(options.identitySize); id.put(options.identity, 0, options.identitySize); id.setFlags(Msg.IDENTITY); boolean written = pipes[0].write(id); assert (written); pipes[0].flush(); // If set, send the hello msg of the local socket to the peer. if (options.canSendHelloMsg && options.helloMsg != null) { written = pipes[0].write(options.helloMsg); assert (written); pipes[0].flush(); } pendConnection(addr, new Ctx.Endpoint(this, options), pipes); } else { // If required, send the identity of the peer to the local socket. if (peer.options.recvIdentity) { Msg id = new Msg(options.identitySize); id.put(options.identity, 0, options.identitySize); id.setFlags(Msg.IDENTITY); boolean written = pipes[0].write(id); assert (written); pipes[0].flush(); } // If required, send the identity of the local socket to the peer. if (options.recvIdentity) { Msg id = new Msg(peer.options.identitySize); id.put(peer.options.identity, 0, peer.options.identitySize); id.setFlags(Msg.IDENTITY); boolean written = pipes[1].write(id); assert (written); pipes[1].flush(); } // If set, send the hello msg of the local socket to the peer. if (options.canSendHelloMsg && options.helloMsg != null) { boolean written = pipes[0].write(options.helloMsg); assert (written); pipes[0].flush(); } // If set, send the hello msg of the peer to the local socket. if (peer.options.canSendHelloMsg && peer.options.helloMsg != null) { boolean written = pipes[1].write(peer.options.helloMsg); assert (written); pipes[1].flush(); } if (peer.options.canReceiveDisconnectMsg && peer.options.disconnectMsg != null) { pipes[0].setDisconnectMsg(peer.options.disconnectMsg); } // Attach remote end of the pipe to the peer socket. Note that peer's // seqnum was incremented in findEndpoint function. We don't need it // increased here. sendBind(peer.socket, pipes[1], false); } // Save last endpoint URI options.lastEndpoint = addr; // remember inproc connections for disconnect inprocs.insert(addr, pipes[0]); return true; } boolean isSingleConnect = options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_SUB || options.type == ZMQ.ZMQ_REQ; if (isSingleConnect) { if (endpoints.hasValues(addr)) { // There is no valid use for multiple connects for SUB-PUB nor // DEALER-ROUTER nor REQ-REP. Multiple connects produces // nonsensical results. return true; } } // Choose the I/O thread to run the session in. IOThread ioThread = chooseIoThread(options.affinity); if (ioThread == null) { errno.set(ZError.EMTHREAD); return false; } Address paddr = new Address(protocol, address); // Resolve address (if needed by the protocol) protocol.resolve(paddr, options.ipv6); // Create session. SessionBase session = Sockets.createSession(ioThread, true, this, options, paddr); assert (session != null); // PGM does not support subscription forwarding; ask for all data to be // sent to this pipe. (same for NORM, currently?) boolean subscribe2all = protocol.subscribe2all; Pipe newpipe = null; if (options.immediate || subscribe2all) { // Create a bi-directional pipe. ZObject[] parents = {this, session}; boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL || options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB); int[] hwms = {conflate ? -1 : options.sendHwm, conflate ? -1 : options.recvHwm}; boolean[] conflates = {conflate, conflate}; Pipe[] pipes = Pipe.pair(parents, hwms, conflates); // Attach local end of the pipe to the socket object. attachPipe(pipes[0], subscribe2all, true); newpipe = pipes[0]; // Attach remote end of the pipe to the session object later on. session.attachPipe(pipes[1]); } // Save last endpoint URI options.lastEndpoint = paddr.toString(); addEndpoint(addr, session, newpipe); return true; } public boolean disconnectPeer(int routingId) { return xdisconnectPeer(routingId); } // Creates new endpoint ID and adds the endpoint to the map. private void addEndpoint(String addr, Own endpoint, Pipe pipe) { // Activate the session. Make it a child of this socket. launchChild(endpoint); endpoints.insert(addr, new EndpointPipe(endpoint, pipe)); } public final boolean termEndpoint(String addr) { lock(); try { // Check whether the library haven't been shut down yet. if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } // Check whether endpoint address passed to the function is valid. if (addr == null) { errno.set(ZError.EINVAL); return false; } // Process pending commands, if any, since there could be pending unprocessed processOwn()'s // (from launchChild() for example) we're asked to terminate now. boolean rc = processCommands(0, false, null); if (!rc) { return false; } SimpleURI uri = SimpleURI.create(addr); NetProtocol protocol = checkProtocol(uri.getProtocol()); if (protocol == null) { return false; } // Disconnect an inproc socket if (protocol == NetProtocol.inproc) { if (unregisterEndpoint(addr, this)) { return true; } Collection olds = inprocs.remove(addr); if (olds == null || olds.isEmpty()) { errno.set(ZError.ENOENT); return false; } else { for (Pipe old : olds) { old.sendDisconnectMsg(); old.terminate(true); } } return true; } String resolvedAddress = addr; // The resolved last_endpoint is used as a key in the endpoints map. // The address passed by the user might not match in the TCP case due to // IPv4-in-IPv6 mapping (EG: tcp://[::ffff:127.0.0.1]:9999), so try to // resolve before giving up. Given at this stage we don't know whether a // socket is connected or bound, try with both. if (protocol == NetProtocol.tcp) { boolean endpoint = endpoints.hasValues(resolvedAddress); if (!endpoint) { // TODO V4 resolve TCP address when unbinding IZAddress address = protocol.zresolve(uri.getAddress(), options.ipv6); resolvedAddress = address.address().toString(); endpoint = endpoints.hasValues(resolvedAddress); if (!endpoint) { // no luck, try with local resolution InetSocketAddress socketAddress = address.resolve(uri.getAddress(), options.ipv6, true); resolvedAddress = socketAddress.toString(); } } } // Find the endpoints range (if any) corresponding to the addr_ string. Collection eps = endpoints.remove(resolvedAddress); if (eps == null || eps.isEmpty()) { errno.set(ZError.ENOENT); return false; } else { // If we have an associated pipe, terminate it. for (EndpointPipe ep : eps) { if (ep.pipe != null) { ep.pipe.terminate(false); } termChild(ep.endpoint); } } return true; } finally { unlock(); } } public final boolean send(Msg msg, int flags) { return send(msg, flags, null); } public final boolean send(Msg msg, int flags, AtomicBoolean canceled) { lock(); try { // Check whether the library haven't been shut down yet. if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } // Check whether message passed to the function is valid. if (msg == null || !msg.check()) { errno.set(ZError.EFAULT); return false; } // Process pending commands, if any. boolean brc = processCommands(0, true, canceled); if (!brc) { return false; } // Clear any user-visible flags that are set on the message. msg.resetFlags(Msg.MORE); // At this point we impose the flags on the message. if ((flags & ZMQ.ZMQ_SNDMORE) > 0) { msg.setFlags(Msg.MORE); } msg.resetMetadata(); // Try to send the message. boolean rc = xsend(msg); if (rc) { return true; } if (errno.get() != ZError.EAGAIN) { return false; } // In case of non-blocking send we'll simply propagate // the error - including EAGAIN - up the stack. if ((flags & ZMQ.ZMQ_DONTWAIT) > 0 || options.sendTimeout == 0) { return false; } // Compute the time when the timeout should occur. // If the timeout is infinite, don't care. int timeout = options.sendTimeout; long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout); // Oops, we couldn't send the message. Wait for the next // command, process it and try to send the message again. // If timeout is reached in the meantime, return EAGAIN. while (true) { if (!processCommands(timeout, false, canceled)) { return false; } rc = xsend(msg); if (rc) { break; } if (errno.get() != ZError.EAGAIN) { return false; } if (timeout > 0) { timeout = (int) (end - Clock.nowMS()); if (timeout <= 0) { errno.set(ZError.EAGAIN); return false; } } } return true; } finally { unlock(); } } public final Msg recv(int flags) { return recv(flags, null); } public final Msg recv(int flags, AtomicBoolean canceled) { lock(); try { // Check whether the library haven't been shut down yet. if (ctxTerminated.get()) { errno.set(ZError.ETERM); return null; } // Check whether message passed to the function is valid.: NOT APPLICABLE // Once every inbound_poll_rate messages check for signals and process // incoming commands. This happens only if we are not polling altogether // because there are messages available all the time. If poll occurs, // ticks is set to zero and thus we avoid this code. // // Note that 'recv' uses different command throttling algorithm (the one // described above) from the one used by 'send'. This is because counting // ticks is more efficient than doing RDTSC all the time. if (++ticks == Config.INBOUND_POLL_RATE.getValue()) { if (!processCommands(0, false, canceled)) { return null; } ticks = 0; } // Get the message. Msg msg = xrecv(); if (msg == null && errno.get() != ZError.EAGAIN) { return null; } // If we have the message, return immediately. if (msg != null) { if (fileDesc != null) { msg.setFd(fileDesc); } extractFlags(msg); return msg; } // If the message cannot be fetched immediately, there are two scenarios. // For non-blocking recv, commands are processed in case there's an // activate_reader command already waiting in a command pipe. // If it's not, return EAGAIN. if ((flags & ZMQ.ZMQ_DONTWAIT) > 0 || options.recvTimeout == 0) { if (!processCommands(0, false, canceled)) { return null; } ticks = 0; msg = xrecv(); if (msg == null) { return null; } extractFlags(msg); return msg; } // Compute the time when the timeout should occur. // If the timeout is infinite, don't care. int timeout = options.recvTimeout; long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout); // In blocking scenario, commands are processed over and over again until // we are able to fetch a message. boolean block = (ticks != 0); while (true) { if (!processCommands(block ? timeout : 0, false, canceled)) { return null; } msg = xrecv(); if (msg != null) { ticks = 0; break; } if (errno.get() != ZError.EAGAIN) { return null; } block = true; if (timeout > 0) { timeout = (int) (end - Clock.nowMS()); if (timeout <= 0) { errno.set(ZError.EAGAIN); return null; } } } extractFlags(msg); return msg; } finally { unlock(); } } public final boolean join(String group) { lock(); try { return xjoin(group); } finally { unlock(); } } public final boolean leave(String group) { lock(); try { return xleave(group); } finally { unlock(); } } public final void cancel(AtomicBoolean canceled) { canceled.set(true); sendCancel(); } public final int poll(int interest, int timeout, AtomicBoolean canceled) { lock(); try { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return -1; } int ready = 0; if (hasOut() && (interest & ZMQ.ZMQ_POLLOUT) > 0) { ready |= ZMQ.ZMQ_POLLOUT; } if (hasIn() && (interest & ZMQ.ZMQ_POLLIN) > 0) { ready |= ZMQ.ZMQ_POLLIN; } if (ready != 0) { return ready; } long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout); while (true) { if (!processCommands(timeout, false, canceled)) { return -1; } ready = 0; if (hasOut() && (interest & ZMQ.ZMQ_POLLOUT) > 0) { ready |= ZMQ.ZMQ_POLLOUT; } if (hasIn() && (interest & ZMQ.ZMQ_POLLIN) > 0) { ready |= ZMQ.ZMQ_POLLIN; } if (ready != 0) { return ready; } if (timeout == 0) { errno.set(ZError.EAGAIN); return -1; } else if (timeout > 0) { timeout = (int) (end - Clock.nowMS()); if (timeout <= 0) { errno.set(ZError.EAGAIN); return -1; } } } } finally { unlock(); } } public final void close() { lock(); try { // Mark the socket as dead active = false; // Transfer the ownership of the socket from this application thread // to the reaper thread which will take care of the rest of shutdown // process. sendReap(this); } finally { unlock(); } } // These functions are used by the polling mechanism to determine // which events are to be reported from this socket. final boolean hasIn() { return xhasIn(); } final boolean hasOut() { return xhasOut(); } // Using this function reaper thread ask the socket to register with // its poller. final void startReaping(Poller poller) { // Plug the socket to the reaper thread. this.poller = poller; SelectableChannel fd; fd = ((Mailbox) mailbox).getFd(); handle = this.poller.addHandle(fd, this); this.poller.setPollIn(handle); // Initialize the termination and check whether it can be deallocated // immediately. ctxTerminated.set(true); terminate(); checkDestroy(); } private boolean isInEvent() { Boolean bRes = isInEventThreadLocal.get(); return null != bRes && bRes; } // Processes commands sent to this socket (if any). If timeout is -1, // returns only after at least one command was processed. // If throttle argument is true, commands are processed at most once // in a predefined time period. private boolean processCommands(int timeout, boolean throttle, AtomicBoolean canceled) { Command cmd; if (timeout != 0) { // If we are asked to wait, simply ask mailbox to wait. cmd = mailbox.recv(timeout); } else { // If we are asked not to wait, check whether we haven't processed // commands recently, so that we can throttle the new commands. // Get the CPU's tick counter. If 0, the counter is not available. long tsc = 0; // Clock.rdtsc(); // Optimized version of command processing - it doesn't have to check // for incoming commands each time. It does so only if certain time // elapsed since last command processing. Command delay varies // depending on CPU speed: It's ~1ms on 3GHz CPU, ~2ms on 1.5GHz CPU // etc. The optimization makes sense only on platforms where getting // a timestamp is a very cheap operation (tens of nanoseconds). if (tsc != 0 && throttle) { // Check whether TSC haven't jumped backwards (in case of migration // between CPU cores) and whether certain time have elapsed since // last command processing. If it didn't do nothing. if (tsc >= lastTsc && tsc - lastTsc <= Config.MAX_COMMAND_DELAY.getValue()) { return true; } lastTsc = tsc; } // Check whether there are any commands pending for this thread. cmd = mailbox.recv(0); } // Process all the commands available at the moment. while (cmd != null) { cmd.process(); cmd = mailbox.recv(0); } if (!isInEvent() && destroyed.get()) { sendReapAck(); } if (errno.get() == ZError.EINTR) { return false; } if (canceled != null && canceled.get()) { errno.set(ZError.ECANCELED); return false; } assert (errno.get() == ZError.EAGAIN) : errno; if (ctxTerminated.get()) { errno.set(ZError.ETERM); // Do not raise exception at the blocked operation return false; } return true; } @Override protected final void processStop() { // Here, someone have called zmq_term while the socket was still alive. // We'll remember the fact so that any blocking call is interrupted and any // further attempt to use the socket will return ETERM. The user is still // responsible for calling zmq_close on the socket though! synchronized (monitor) { stopMonitor(); ctxTerminated.set(true); } } @Override protected final void processBind(Pipe pipe) { attachPipe(pipe, false); } @Override protected final void processTerm(int linger) { // Unregister all inproc endpoints associated with this socket. // Doing this we make sure that no new pipes from other sockets (inproc) // will be initiated. unregisterEndpoints(this); // Ask all attached pipes to terminate. for (Pipe pipe : pipes) { pipe.sendDisconnectMsg(); pipe.terminate(false); } registerTermAcks(pipes.size()); // Continue the termination process immediately. super.processTerm(linger); } // Delay actual destruction of the socket. @Override protected final void processDestroy() { destroyed.set(true); } // The default implementation assumes there are no specific socket // options for the particular socket type. If not so, overload this // method. protected boolean xsetsockopt(int option, Object optval) { errno.set(ZError.EINVAL); return false; } protected boolean xhasOut() { return false; } protected boolean xsend(Msg msg) { throw new UnsupportedOperationException("Must Override"); } protected boolean xhasIn() { return false; } protected Msg xrecv() { throw new UnsupportedOperationException("Must Override"); } protected Blob getCredential() { throw new UnsupportedOperationException("Must Override"); } protected void xreadActivated(Pipe pipe) { throw new UnsupportedOperationException("Must Override"); } protected void xwriteActivated(Pipe pipe) { throw new UnsupportedOperationException("Must Override"); } protected void xhiccuped(Pipe pipe) { throw new UnsupportedOperationException("Must override"); } protected boolean xjoin(String group) { throw new UnsupportedOperationException("Must override"); } protected boolean xleave(String group) { throw new UnsupportedOperationException("Must override"); } protected boolean xdisconnectPeer(int routingId) { throw new UnsupportedOperationException("Must override"); } private void enterInEvent() { isInEventThreadLocal.set(true); } private void leaveInEvent() { isInEventThreadLocal.remove(); } @Override public final void inEvent() { // This function is invoked only once the socket is running in the context // of the reaper thread. Process any commands from other threads/sockets // that may be available at the moment. Ultimately, the socket will // be destroyed. lock(); try { enterInEvent(); processCommands(0, false, null); } finally { leaveInEvent(); unlock(); } checkDestroy(); } // To be called after processing commands or invoking any command // handlers explicitly. If required, it will deallocate the socket. private void checkDestroy() { // If the object was already marked as destroyed, finish the deallocation. if (destroyed.get()) { // Remove the socket from the reaper's poller. poller.removeHandle(handle); // Remove the socket from the context. destroySocket(this); // Notify the reaper about the fact. sendReaped(); // Deallocate. super.processDestroy(); } } @Override public final void readActivated(Pipe pipe) { xreadActivated(pipe); } @Override public final void writeActivated(Pipe pipe) { xwriteActivated(pipe); } @Override public final void hiccuped(Pipe pipe) { if (!options.immediate) { pipe.terminate(false); } else { // Notify derived sockets of the hiccup xhiccuped(pipe); } } @Override public final void pipeTerminated(Pipe pipe) { // Notify the specific socket type about the pipe termination. xpipeTerminated(pipe); // Remove pipe from inproc pipes inprocs.remove(pipe); // Remove the pipe from the list of attached pipes and confirm its // termination if we are already shutting down. pipes.remove(pipe); if (isTerminating()) { unregisterTermAck(); } } // Moves the flags from the message to local variables, // to be later retrieved by getSocketOpt. private void extractFlags(Msg msg) { // Test whether IDENTITY flag is valid for this socket type. assert !msg.isIdentity() || (options.recvIdentity); // Remove MORE flag. rcvmore = msg.hasMore(); } /** * Register the address for a monitor. It must be a inproc PAIR. * @param addr or null for unregister. * @param events an event mask to monitor. * @return true if creation succeeded. * @throws IllegalStateException if a previous monitor was already * registered and consumer is not null. */ public final boolean monitor(final String addr, int events) { synchronized (monitor) { // To be tested before trying anything if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } // Support unregistering monitoring endpoints as well if (addr == null) { stopMonitor(); return true; } SimpleURI uri = SimpleURI.create(addr); NetProtocol protocol = checkProtocol(uri.getProtocol()); if (protocol == null) { return false; } // Event notification only supported over inproc:// if (protocol != NetProtocol.inproc) { errno.set(ZError.EPROTONOSUPPORT); return false; } SocketBase monitorSocket = getCtx().createSocket(ZMQ.ZMQ_PAIR); if (monitorSocket == null) { return false; } // Never block context termination on pending event messages monitorSocket.setSocketOpt(ZMQ.ZMQ_LINGER, 0); boolean rc = monitorSocket.bind(addr); if (rc) { return setEventHook(new SocketEventHandler(monitorSocket), events); } else { return false; } } } /** * Register a custom event consumer. * @param consumer or null for unregister. * @param events an event mask to monitor. * @return true if creation succeeded. * @throws IllegalStateException if a previous monitor was already * registered and consumer is not null. */ public final boolean setEventHook(ZMQ.EventConsummer consumer, int events) { synchronized (monitor) { if (ctxTerminated.get()) { errno.set(ZError.ETERM); return false; } // Support unregistering monitoring endpoints as well if (consumer == null) { stopMonitor(); } else { if (monitor.get() != null) { throw new IllegalStateException("Monitor registered twice"); } monitor.set(consumer); // Register events to monitor monitorEvents = events; } return true; } } public final void eventHandshaken(String addr, int zmtpVersion) { event(addr, zmtpVersion, ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL); } public final void eventConnected(String addr, SelectableChannel ch) { event(addr, ch, ZMQ.ZMQ_EVENT_CONNECTED); } public final void eventConnectDelayed(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_CONNECT_DELAYED); } public final void eventConnectRetried(String addr, int interval) { event(addr, interval, ZMQ.ZMQ_EVENT_CONNECT_RETRIED); } public final void eventListening(String addr, SelectableChannel ch) { event(addr, ch, ZMQ.ZMQ_EVENT_LISTENING); } public final void eventBindFailed(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_BIND_FAILED); } public final void eventAccepted(String addr, SelectableChannel ch) { event(addr, ch, ZMQ.ZMQ_EVENT_ACCEPTED); } public final void eventAcceptFailed(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_ACCEPT_FAILED); } public final void eventClosed(String addr, SelectableChannel ch) { event(addr, ch, ZMQ.ZMQ_EVENT_CLOSED); } public final void eventCloseFailed(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_CLOSE_FAILED); } public final void eventDisconnected(String addr, SelectableChannel ch) { event(addr, ch, ZMQ.ZMQ_EVENT_DISCONNECTED); } public final void eventHandshakeFailedNoDetail(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL); } public final void eventHandshakeFailedProtocol(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL); } public final void eventHandshakeFailedAuth(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_AUTH); } public final void eventHandshakeSucceeded(String addr, int errno) { event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED); } private void event(String addr, Object arg, int event) { synchronized (monitor) { if ((monitorEvents & event) == 0 || monitor.get() == null) { return; } monitorEvent(new ZMQ.Event(event, addr, arg)); } } // Send a monitor event protected final void monitorEvent(ZMQ.Event event) { if (monitor.get() == null) { return; } monitor.get().consume(event); } private void stopMonitor() { // this is a private method which is only called from // contexts where the mutex has been locked before if (monitor.get() != null) { if ((monitorEvents & ZMQ.ZMQ_EVENT_MONITOR_STOPPED) != 0) { monitorEvent(new ZMQ.Event(ZMQ.ZMQ_EVENT_MONITOR_STOPPED, "", 0)); } monitor.get().close(); monitor.set(null); monitorEvents = 0; } } @Override public String toString() { return getClass().getSimpleName() + "[" + options.socketId + "]"; } public final SelectableChannel getFD() { if (threadSafe) { errno.set(ZError.EINVAL); return null; } return ((Mailbox) mailbox).getFd(); } public String typeString() { return Sockets.name(options.type); } public final int errno() { return errno.get(); } private void lock() { if (threadSafe) { threadSafeSync.lock(); } } private void unlock() { if (threadSafe) { threadSafeSync.unlock(); } } private static class SimpleURI { private final String protocol; private final String address; private SimpleURI(String protocol, String address) { this.protocol = protocol; this.address = address; } public static SimpleURI create(String value) { int pos = value.indexOf("://"); if (pos < 0) { throw new IllegalArgumentException("Invalid URI: " + value); } String protocol = value.substring(0, pos); String address = value.substring(pos + 3); if (protocol.isEmpty() || address.isEmpty()) { throw new IllegalArgumentException("Invalid URI: " + value); } return new SimpleURI(protocol, address); } public String getProtocol() { return protocol; } public String getAddress() { return address; } } } jeromq-0.6.0/src/main/java/zmq/Utils.java000066400000000000000000000025331455771126300202360ustar00rootroot00000000000000package zmq; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import zmq.io.net.Address; import zmq.io.net.tcp.TcpUtils; @Deprecated public class Utils { private Utils() { } public static int randomInt() { return zmq.util.Utils.randomInt(); } public static byte[] randomBytes(int length) { return zmq.util.Utils.randomBytes(length); } public static int findOpenPort() throws IOException { return zmq.util.Utils.findOpenPort(); } public static void unblockSocket(SelectableChannel... channels) throws IOException { TcpUtils.unblockSocket(channels); } public static T[] realloc(Class klass, T[] src, int size, boolean ended) { return zmq.util.Utils.realloc(klass, src, size, ended); } public static byte[] bytes(ByteBuffer buf) { return zmq.util.Utils.bytes(buf); } public static byte[] realloc(byte[] src, int size) { return zmq.util.Utils.realloc(src, size); } public static boolean delete(File path) { return zmq.util.Utils.delete(path); } public static Address getPeerIpAddress(SocketChannel fd) { return zmq.util.Utils.getPeerIpAddress(fd); } } jeromq-0.6.0/src/main/java/zmq/ZError.java000066400000000000000000000072441455771126300203650ustar00rootroot00000000000000package zmq; import java.net.SocketException; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedChannelException; import org.zeromq.ZMQ.Error; import org.zeromq.UncheckedZMQException; public class ZError { private ZError() { } public static class CtxTerminatedException extends UncheckedZMQException { private static final long serialVersionUID = -4404921838608052956L; public CtxTerminatedException() { super(); } } public static class InstantiationException extends UncheckedZMQException { private static final long serialVersionUID = -4404921838608052955L; public InstantiationException(Throwable cause) { super(cause); } public InstantiationException(String message, Throwable cause) { super(message, cause); } public InstantiationException(String message) { super(message); } } public static class IOException extends UncheckedZMQException { private static final long serialVersionUID = 9202470691157986262L; public IOException(java.io.IOException e) { super(e); } } public static final int ENOENT = 2; public static final int EINTR = 4; public static final int EACCESS = 13; public static final int EFAULT = 14; public static final int EINVAL = 22; public static final int EAGAIN = 35; public static final int EINPROGRESS = 36; public static final int EPROTONOSUPPORT = 43; public static final int ENOTSUP = 45; public static final int EADDRINUSE = 48; public static final int EADDRNOTAVAIL = 49; public static final int ENETDOWN = 50; public static final int ENOBUFS = 55; public static final int EISCONN = 56; public static final int ENOTCONN = 57; public static final int ECONNREFUSED = 61; public static final int EHOSTUNREACH = 65; public static final int ECANCELED = 125; private static final int ZMQ_HAUSNUMERO = 156384712; public static final int ENOTSOCK = ZMQ_HAUSNUMERO + 5; public static final int EMSGSIZE = ZMQ_HAUSNUMERO + 10; public static final int EAFNOSUPPORT = ZMQ_HAUSNUMERO + 11; public static final int ENETUNREACH = ZMQ_HAUSNUMERO + 12; public static final int ECONNABORTED = ZMQ_HAUSNUMERO + 13; public static final int ECONNRESET = ZMQ_HAUSNUMERO + 14; public static final int ETIMEDOUT = ZMQ_HAUSNUMERO + 16; public static final int ENETRESET = ZMQ_HAUSNUMERO + 18; public static final int EFSM = ZMQ_HAUSNUMERO + 51; public static final int ENOCOMPATPROTO = ZMQ_HAUSNUMERO + 52; public static final int ETERM = ZMQ_HAUSNUMERO + 53; public static final int EMTHREAD = ZMQ_HAUSNUMERO + 54; public static final int EIOEXC = ZMQ_HAUSNUMERO + 105; public static final int ESOCKET = ZMQ_HAUSNUMERO + 106; public static final int EMFILE = ZMQ_HAUSNUMERO + 107; public static final int EPROTO = ZMQ_HAUSNUMERO + 108; public static int exccode(java.io.IOException e) { if (e instanceof SocketException) { return ESOCKET; } else if (e instanceof ClosedByInterruptException) { return EINTR; } else if (e instanceof ClosedChannelException) { return ENOTCONN; } else { return EIOEXC; } } public static String toString(int code) { return Error.findByCode(code).getMessage(); } } jeromq-0.6.0/src/main/java/zmq/ZMQ.java000066400000000000000000001166061455771126300176140ustar00rootroot00000000000000package zmq; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import zmq.io.Metadata; import zmq.io.mechanism.Mechanisms; import zmq.io.net.SelectorProviderChooser; import zmq.msg.MsgAllocator; import zmq.msg.MsgAllocatorThreshold; import zmq.poll.PollItem; import zmq.util.Clock; import zmq.util.Utils; public class ZMQ { /******************************************************************************/ /* 0MQ versioning support. */ /******************************************************************************/ /* Version macros for compile-time API version detection */ public static final int ZMQ_VERSION_MAJOR = 4; public static final int ZMQ_VERSION_MINOR = 1; public static final int ZMQ_VERSION_PATCH = 7; /* Context options */ public static final int ZMQ_IO_THREADS = 1; public static final int ZMQ_MAX_SOCKETS = 2; /* Default for new contexts */ public static final int ZMQ_IO_THREADS_DFLT = 1; public static final int ZMQ_MAX_SOCKETS_DFLT = 1024; /******************************************************************************/ /* 0MQ socket definition. */ /******************************************************************************/ /* Socket types. */ public static final int ZMQ_PAIR = 0; public static final int ZMQ_PUB = 1; public static final int ZMQ_SUB = 2; public static final int ZMQ_REQ = 3; public static final int ZMQ_REP = 4; public static final int ZMQ_DEALER = 5; public static final int ZMQ_ROUTER = 6; public static final int ZMQ_PULL = 7; public static final int ZMQ_PUSH = 8; public static final int ZMQ_XPUB = 9; public static final int ZMQ_XSUB = 10; public static final int ZMQ_STREAM = 11; public static final int ZMQ_SERVER = 12; public static final int ZMQ_CLIENT = 13; public static final int ZMQ_RADIO = 14; public static final int ZMQ_DISH = 15; public static final int ZMQ_CHANNEL = 16; public static final int ZMQ_PEER = 17; public static final int ZMQ_RAW = 18; public static final int ZMQ_SCATTER = 19; public static final int ZMQ_GATHER = 20; /* Deprecated aliases */ @Deprecated public static final int ZMQ_XREQ = ZMQ_DEALER; @Deprecated public static final int ZMQ_XREP = ZMQ_ROUTER; private static final int ZMQ_CUSTOM_OPTION = 1000; /* Socket options. */ public static final int ZMQ_AFFINITY = 4; public static final int ZMQ_IDENTITY = 5; public static final int ZMQ_SUBSCRIBE = 6; public static final int ZMQ_UNSUBSCRIBE = 7; public static final int ZMQ_RATE = 8; public static final int ZMQ_RECOVERY_IVL = 9; public static final int ZMQ_SNDBUF = 11; public static final int ZMQ_RCVBUF = 12; public static final int ZMQ_RCVMORE = 13; public static final int ZMQ_FD = 14; public static final int ZMQ_EVENTS = 15; public static final int ZMQ_TYPE = 16; public static final int ZMQ_LINGER = 17; public static final int ZMQ_RECONNECT_IVL = 18; public static final int ZMQ_BACKLOG = 19; public static final int ZMQ_RECONNECT_IVL_MAX = 21; public static final int ZMQ_MAXMSGSIZE = 22; public static final int ZMQ_SNDHWM = 23; public static final int ZMQ_RCVHWM = 24; public static final int ZMQ_MULTICAST_HOPS = 25; public static final int ZMQ_RCVTIMEO = 27; public static final int ZMQ_SNDTIMEO = 28; public static final int ZMQ_LAST_ENDPOINT = 32; public static final int ZMQ_ROUTER_MANDATORY = 33; public static final int ZMQ_TCP_KEEPALIVE = 34; public static final int ZMQ_TCP_KEEPALIVE_CNT = 35; public static final int ZMQ_TCP_KEEPALIVE_IDLE = 36; public static final int ZMQ_TCP_KEEPALIVE_INTVL = 37; public static final int ZMQ_IMMEDIATE = 39 + ZMQ_CUSTOM_OPTION; // for compatibility with ZMQ_DELAY_ATTACH_ON_CONNECT public static final int ZMQ_XPUB_VERBOSE = 40; public static final int ZMQ_ROUTER_RAW = 41; public static final int ZMQ_IPV6 = 42; public static final int ZMQ_MECHANISM = 43; public static final int ZMQ_PLAIN_SERVER = 44; public static final int ZMQ_PLAIN_USERNAME = 45; public static final int ZMQ_PLAIN_PASSWORD = 46; public static final int ZMQ_CURVE_SERVER = 47; public static final int ZMQ_CURVE_PUBLICKEY = 48; public static final int ZMQ_CURVE_SECRETKEY = 49; public static final int ZMQ_CURVE_SERVERKEY = 50; public static final int ZMQ_PROBE_ROUTER = 51; public static final int ZMQ_REQ_CORRELATE = 52; public static final int ZMQ_REQ_RELAXED = 53; public static final int ZMQ_CONFLATE = 54; public static final int ZMQ_ZAP_DOMAIN = 55; // TODO: more constants public static final int ZMQ_ROUTER_HANDOVER = 56; public static final int ZMQ_TOS = 57; public static final int ZMQ_CONNECT_RID = 61; public static final int ZMQ_GSSAPI_SERVER = 62; public static final int ZMQ_GSSAPI_PRINCIPAL = 63; public static final int ZMQ_GSSAPI_SERVICE_PRINCIPAL = 64; public static final int ZMQ_GSSAPI_PLAINTEXT = 65; public static final int ZMQ_HANDSHAKE_IVL = 66; public static final int ZMQ_SOCKS_PROXY = 67; public static final int ZMQ_XPUB_NODROP = 69; public static final int ZMQ_BLOCKY = 70; public static final int ZMQ_XPUB_MANUAL = 71; public static final int ZMQ_HEARTBEAT_IVL = 75; public static final int ZMQ_HEARTBEAT_TTL = 76; public static final int ZMQ_HEARTBEAT_TIMEOUT = 77; public static final int ZMQ_XPUB_VERBOSER = 78; @Deprecated public static final int ZMQ_XPUB_VERBOSE_UNSUBSCRIBE = 78; public static final int ZMQ_HELLO_MSG = 79; public static final int ZMQ_AS_TYPE = 80; public static final int ZMQ_DISCONNECT_MSG = 81; public static final int ZMQ_HICCUP_MSG = 82; public static final int ZMQ_SELFADDR_PROPERTY_NAME = 83; /* Custom options */ @Deprecated public static final int ZMQ_ENCODER = ZMQ_CUSTOM_OPTION + 1; @Deprecated public static final int ZMQ_DECODER = ZMQ_CUSTOM_OPTION + 2; public static final int ZMQ_MSG_ALLOCATOR = ZMQ_CUSTOM_OPTION + 3; public static final int ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD = ZMQ_CUSTOM_OPTION + 4; public static final int ZMQ_HEARTBEAT_CONTEXT = ZMQ_CUSTOM_OPTION + 5; public static final int ZMQ_SELECTOR_PROVIDERCHOOSER = ZMQ_CUSTOM_OPTION + 6; /* Message options */ public static final int ZMQ_MORE = 1; /* Send/recv options. */ public static final int ZMQ_DONTWAIT = 1; public static final int ZMQ_SNDMORE = 2; /* Deprecated aliases */ @Deprecated public static final int ZMQ_TCP_ACCEPT_FILTER = 38; @Deprecated public static final int ZMQ_IPV4ONLY = 31; @Deprecated public static final int ZMQ_DELAY_ATTACH_ON_CONNECT = 39; @Deprecated public static final int ZMQ_NOBLOCK = ZMQ_DONTWAIT; @Deprecated public static final int ZMQ_FAIL_UNROUTABLE = ZMQ_ROUTER_MANDATORY; @Deprecated public static final int ZMQ_ROUTER_BEHAVIOR = ZMQ_ROUTER_MANDATORY; /******************************************************************************/ /* 0MQ socket events and monitoring */ /******************************************************************************/ /* Socket transport events (tcp and ipc only) */ public static final int ZMQ_EVENT_CONNECTED = 1; public static final int ZMQ_EVENT_CONNECT_DELAYED = 1 << 1; public static final int ZMQ_EVENT_CONNECT_RETRIED = 1 << 2; public static final int ZMQ_EVENT_LISTENING = 1 << 3; public static final int ZMQ_EVENT_BIND_FAILED = 1 << 4; public static final int ZMQ_EVENT_ACCEPTED = 1 << 5; public static final int ZMQ_EVENT_ACCEPT_FAILED = 1 << 6; public static final int ZMQ_EVENT_CLOSED = 1 << 7; public static final int ZMQ_EVENT_CLOSE_FAILED = 1 << 8; public static final int ZMQ_EVENT_DISCONNECTED = 1 << 9; public static final int ZMQ_EVENT_MONITOR_STOPPED = 1 << 10; public static final int ZMQ_EVENT_HANDSHAKE_PROTOCOL = 1 << 15; public static final int ZMQ_EVENT_ALL = 0xffff; /* Unspecified system errors during handshake. Event value is an errno. */ public static final int ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL = 1 << 11; /* Handshake complete successfully with successful authentication (if * * enabled). Event value is unused. */ public static final int ZMQ_EVENT_HANDSHAKE_SUCCEEDED = 1 << 12; /* Protocol errors between ZMTP peers or between server and ZAP handler. * * Event value is one of ZMQ_PROTOCOL_ERROR_* */ public static final int ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL = 1 << 13; /* Failed authentication requests. Event value is the numeric ZAP status * * code, i.e. 300, 400 or 500. */ public static final int ZMQ_EVENT_HANDSHAKE_FAILED_AUTH = 1 << 14; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED = 0x10000000; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND = 0x10000001; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE = 0x10000002; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE = 0x10000003; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED = 0x10000011; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE = 0x10000012; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO = 0x10000013; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE = 0x10000014; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR = 0x10000015; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY = 0x10000016; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME = 0x10000017; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA = 0x10000018; // the following two may be due to erroneous configuration of a peer public static final int ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC = 0x11000001; public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH = 0x11000002; public static final int ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED = 0x20000000; public static final int ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY = 0x20000001; public static final int ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID = 0x20000002; public static final int ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION = 0x20000003; public static final int ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE = 0x20000004; public static final int ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA = 0x20000005; public static final int ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED = 0x30000000; public static final int ZMQ_POLLIN = 1; public static final int ZMQ_POLLOUT = 2; public static final int ZMQ_POLLERR = 4; @Deprecated public static final int ZMQ_STREAMER = 1; @Deprecated public static final int ZMQ_FORWARDER = 2; @Deprecated public static final int ZMQ_QUEUE = 3; public static final byte[] MESSAGE_SEPARATOR = new byte[0]; public static final byte[] SUBSCRIPTION_ALL = new byte[0]; // Android compatibility: not using StandardCharsets (API 19+) public static final Charset CHARSET = StandardCharsets.UTF_8; public static final byte[] PROXY_PAUSE = "PAUSE".getBytes(ZMQ.CHARSET); public static final byte[] PROXY_RESUME = "RESUME".getBytes(ZMQ.CHARSET); public static final byte[] PROXY_TERMINATE = "TERMINATE".getBytes(ZMQ.CHARSET); // Default values for options /** * Default value for {@link ZMQ#ZMQ_AFFINITY} */ public static final int DEFAULT_AFFINITY = 0; /** * Default value for {@link ZMQ#ZMQ_SERVER} */ public static final boolean DEFAULT_AS_SERVER = false; /** * Default value for {@link ZMQ#ZMQ_AS_TYPE} */ public static final int DEFAULT_AS_TYPE = -1; /** * Default value for {@link ZMQ#ZMQ_BACKLOG} */ public static final int DEFAULT_BACKLOG = 100; /** * Default value for {@link ZMQ#ZMQ_CONFLATE} */ public static final boolean DEFAULT_CONFLATE = false; /** * Default value for {@link ZMQ#ZMQ_DISCONNECT_MSG} */ public static final Msg DEFAULT_DISCONNECT_MSG = null; /** * Default value for {@link ZMQ#ZMQ_GSSAPI_PLAINTEXT} */ public static final boolean DEFAULT_GSS_PLAINTEXT = false; /** * Default value for {@link ZMQ#ZMQ_HANDSHAKE_IVL} */ public static final int DEFAULT_HANDSHAKE_IVL = 30000; /** * Default value for {@link ZMQ#ZMQ_HEARTBEAT_CONTEXT} */ public static final byte[] DEFAULT_HEARTBEAT_CONTEXT = new byte[0]; /** * Default value for {@link ZMQ#ZMQ_HEARTBEAT_IVL} */ public static final int DEFAULT_HEARTBEAT_INTERVAL = 0; /** * Default value for {@link ZMQ#ZMQ_HEARTBEAT_TIMEOUT} */ public static final int DEFAULT_HEARTBEAT_TIMEOUT = -1; /** * Default value for {@link ZMQ#ZMQ_HEARTBEAT_TTL} */ public static final int DEFAULT_HEARTBEAT_TTL = 0; /** * Default value for {@link ZMQ#ZMQ_HELLO_MSG} */ public static final Msg DEFAULT_HELLO_MSG = null; /** * Default value for {@link ZMQ#ZMQ_HICCUP_MSG} */ public static final Msg DEFAULT_HICCUP_MSG = null; /** * Default value for {@link ZMQ#ZMQ_IDENTITY} */ public static final byte[] DEFAULT_IDENTITY = new byte[0]; /** * Default value for {@link ZMQ#ZMQ_IMMEDIATE} */ public static final boolean DEFAULT_IMMEDIATE = true; /** * Default value for {@link ZMQ#ZMQ_IPV6} */ public static final boolean DEFAULT_IPV6 = ZMQ.PREFER_IPV6; /** * Default value for {@link ZMQ#ZMQ_LINGER} */ public static final int DEFAULT_LINGER = -1; /** * Default value for {@link ZMQ#ZMQ_MAXMSGSIZE} */ public static final long DEFAULT_MAX_MSG_SIZE = -1; /** * Default value for {@link ZMQ#ZMQ_MECHANISM} */ public static final Mechanisms DEFAULT_MECHANISM = Mechanisms.NULL; /** * Default value for {@link ZMQ#ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD} */ public static final int DEFAULT_ALLOCATION_HEAP_THRESHOLD = Config.MSG_ALLOCATION_HEAP_THRESHOLD.getValue(); /** * Default value for {@link ZMQ#ZMQ_MSG_ALLOCATOR} */ public static final MsgAllocator DEFAULT_MSG_ALLOCATOR = new MsgAllocatorThreshold(DEFAULT_ALLOCATION_HEAP_THRESHOLD); /** * Default value for {@link ZMQ#ZMQ_RECONNECT_IVL} */ public static final int DEFAULT_RECONNECT_IVL = 100; /** * Default value for {@link ZMQ#ZMQ_RECONNECT_IVL_MAX} */ public static final int DEFAULT_RECONNECT_IVL_MAX = 0; /** * Default value for {@link ZMQ#ZMQ_RCVHWM} */ public static final int DEFAULT_RECV_HWM = 1000; /** * Default value for {@link ZMQ#ZMQ_RCVTIMEO} */ public static final int DEFAULT_RECV_TIMEOUT = -1; /** * Default value for {@link ZMQ#ZMQ_RCVBUF} */ public static final int DEFAULT_RCVBUF = 0; /** * Default value for {@link ZMQ#ZMQ_RATE} */ public static final int DEFAULT_RATE = 100; /** * Default value for {@link ZMQ#ZMQ_RECOVERY_IVL} */ public static final int DEFAULT_RECOVERY_IVL = 10000; /** * Default value for {@link ZMQ#ZMQ_SELFADDR_PROPERTY_NAME} */ public static final String DEFAULT_SELF_ADDRESS_PROPERTY_NAME = null; /** * Default value for {@link ZMQ#ZMQ_SNDHWM} */ public static final int DEFAULT_SEND_HWM = 1000; /** * Default value for {@link ZMQ#ZMQ_SNDTIMEO} */ public static final int DEFAULT_SEND_TIMEOUT = -1; /** * Default value for {@link ZMQ#ZMQ_SELECTOR_PROVIDERCHOOSER} */ public static final SelectorProviderChooser DEFAULT_SELECTOR_CHOOSER = null; /** * Default value for {@link ZMQ#ZMQ_SNDBUF} */ public static final int DEFAULT_SNDBUF = 0; /** * Default value for {@link ZMQ#ZMQ_SOCKS_PROXY} */ public static final String DEFAULT_SOCKS_PROXY_ADDRESS = null; /** * Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE} */ public static final int DEFAULT_TCP_KEEP_ALIVE = -1; /** * Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_CNT} */ public static final int DEFAULT_TCP_KEEP_ALIVE_CNT = -1; /** * Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_IDLE} */ public static final int DEFAULT_TCP_KEEP_ALIVE_IDLE = -1; /** * Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_INTVL} */ public static final int DEFAULT_TCP_KEEP_ALIVE_INTVL = -1; /** * Default value for {@link ZMQ#ZMQ_TOS} */ public static final int DEFAULT_TOS = 0; /** * Default value for {@link ZMQ#ZMQ_TYPE} */ public static final int DEFAULT_TYPE = -1; /** * Default value for {@link ZMQ#ZMQ_MULTICAST_HOPS} */ public static final int DEFAULT_MULTICAST_HOPS = 1; /** * Default value for {@link ZMQ#ZMQ_ZAP_DOMAIN} */ public static final String DEFAULT_ZAP_DOMAIN = ""; public static final boolean PREFER_IPV6; static { String preferIPv4Stack = System.getProperty("java.net.preferIPv4Stack"); String preferIPv6Addresses = System.getProperty("java.net.preferIPv6Addresses"); PREFER_IPV6 = "false".equalsIgnoreCase(preferIPv4Stack) || "true".equalsIgnoreCase(preferIPv6Addresses); } /** * An interface used to consume events in monitor */ public interface EventConsummer { void consume(Event ev); /** * An optional method to close the monitor if needed */ default void close() { // Default do nothing } } public static class Event { private static final int VALUE_INTEGER = 1; private static final int VALUE_CHANNEL = 2; public final int event; public final String addr; public final Object arg; private final int flag; public Event(int event, String addr, Object arg) { this.event = event; this.addr = addr; this.arg = arg; if (arg instanceof Integer) { flag = VALUE_INTEGER; } else if (arg instanceof SelectableChannel) { flag = VALUE_CHANNEL; } else { flag = 0; } } private Event(int event, String addr, Object arg, int flag) { this.event = event; this.addr = addr; this.arg = arg; this.flag = flag; } public boolean write(SocketBase s) { Msg msg = new Msg(serialize(s.getCtx())); return s.send(msg, 0); } private ByteBuffer serialize(Ctx ctx) { int size = 4 + 1 + addr.length() + 1; // event + len(addr) + addr + flag if (flag == VALUE_INTEGER || flag == VALUE_CHANNEL) { size += 4; } ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.BIG_ENDIAN); buffer.putInt(event); buffer.put((byte) addr.length()); buffer.put(addr.getBytes(CHARSET)); buffer.put((byte) flag); if (flag == VALUE_INTEGER) { buffer.putInt((Integer) arg); } else if (flag == VALUE_CHANNEL) { int channeldId = ctx.forwardChannel((SelectableChannel) arg); buffer.putInt(channeldId); } buffer.flip(); return buffer; } /** * Resolve the channel that was associated with this event. * Implementation note: to be backward compatible, {@link #arg} only store Integer value, so * the channel is resolved using this call. *

* Internally socket are kept using weak values, so it's better to retrieve the channel as early * as possible, otherwise it might get lost. * * @param socket the socket that send the event * @return the channel in the event, or null if was not a channel event. */ public SelectableChannel getChannel(SocketBase socket) { return getChannel(socket.getCtx()); } /** * Resolve the channel that was associated with this event. * Implementation note: to be backward compatible, {@link #arg} only store Integer value, so * the channel is resolved using this call. *

* Internally socket are kept using weak values, so it's better to retrieve the channel as early * as possible, otherwise it might get lost. * * @param ctx the socket that send the event * @return the channel in the event, or null if was not a channel event. */ public SelectableChannel getChannel(Ctx ctx) { if (flag == VALUE_CHANNEL) { return ctx.getForwardedChannel((Integer) arg); } else { return null; } } public static Event read(SocketBase s, int flags) { Msg msg = s.recv(flags); if (msg == null) { return null; } ByteBuffer buffer = msg.buf(); int event = buffer.getInt(); int len = buffer.get(); byte[] addr = new byte[len]; buffer.get(addr); int flag = buffer.get(); Object arg = null; if (flag == VALUE_INTEGER || flag == VALUE_CHANNEL) { arg = buffer.getInt(); } return new Event(event, new String(addr, CHARSET), arg, flag); } public static Event read(SocketBase s) { return read(s, 0); } } // New context API public static Ctx createContext() { // Create 0MQ context. return new Ctx(); } private static void checkContext(Ctx ctx) { if (ctx == null || !ctx.isActive()) { throw new IllegalStateException(); } } private static void destroyContext(Ctx ctx) { checkContext(ctx); ctx.terminate(); } public static void setContextOption(Ctx ctx, int option, int optval) { checkContext(ctx); ctx.set(option, optval); } public static int getContextOption(Ctx ctx, int option) { checkContext(ctx); return ctx.get(option); } // Stable/legacy context API public static Ctx init(int ioThreads) { Utils.checkArgument(ioThreads >= 0, "I/O threads must not be negative"); Ctx ctx = createContext(); setContextOption(ctx, ZMQ_IO_THREADS, ioThreads); return ctx; } public static void term(Ctx ctx) { destroyContext(ctx); } // Sockets public static SocketBase socket(Ctx ctx, int type) { checkContext(ctx); return ctx.createSocket(type); } private static void checkSocket(SocketBase s) { if (s == null || !s.isActive()) { throw new IllegalStateException(); } } public static void closeZeroLinger(SocketBase s) { checkSocket(s); s.setSocketOpt(ZMQ.ZMQ_LINGER, 0); s.close(); } public static void close(SocketBase s) { checkSocket(s); s.close(); } public static boolean setSocketOption(SocketBase s, int option, Object optval) { checkSocket(s); return s.setSocketOpt(option, optval); } public static Object getSocketOptionExt(SocketBase s, int option) { checkSocket(s); return s.getSocketOptx(option); } public static int getSocketOption(SocketBase s, int opt) { return s.getSocketOpt(opt); } public static boolean monitorSocket(SocketBase s, final String addr, int events) { checkSocket(s); return s.monitor(addr, events); } public static boolean bind(SocketBase s, final String addr) { checkSocket(s); return s.bind(addr); } public static boolean connect(SocketBase s, String addr) { checkSocket(s); return s.connect(addr); } public static int connectPeer(SocketBase s, String addr) { checkSocket(s); return s.connectPeer(addr); } public static boolean disconnectPeer(SocketBase s, int routingId) { checkSocket(s); return s.disconnectPeer(routingId); } public static boolean unbind(SocketBase s, String addr) { checkSocket(s); return s.termEndpoint(addr); } public static boolean disconnect(SocketBase s, String addr) { checkSocket(s); return s.termEndpoint(addr); } // Sending functions. public static int send(SocketBase s, String str, int flags) { byte[] data = str.getBytes(CHARSET); return send(s, data, data.length, flags); } public static int send(SocketBase s, Msg msg, int flags) { int rc = sendMsg(s, msg, flags); if (rc < 0) { return -1; } return rc; } public static int send(SocketBase s, byte[] buf, int flags) { return send(s, buf, buf.length, flags); } public static int send(SocketBase s, byte[] buf, int len, int flags) { checkSocket(s); Msg msg = new Msg(len); msg.put(buf, 0, len); int rc = sendMsg(s, msg, flags); if (rc < 0) { return -1; } return rc; } // Send multiple messages. // // If flag bit ZMQ_SNDMORE is set the vector is treated as // a single multi-part message, i.e. the last message has // ZMQ_SNDMORE bit switched off. // public int sendiov(SocketBase s, byte[][] a, int count, int flags) { checkSocket(s); int rc = 0; Msg msg; for (int i = 0; i < count; ++i) { msg = new Msg(a[i]); if (i == count - 1) { flags = flags & ~ZMQ_SNDMORE; } rc = sendMsg(s, msg, flags); if (rc < 0) { rc = -1; break; } } return rc; } public static boolean sendMsg(SocketBase socket, byte[]... data) { int rc; if (data.length == 0) { return false; } for (int idx = 0; idx < data.length - 1; ++idx) { rc = send(socket, new Msg(data[idx]), ZMQ_MORE); if (rc < 0) { return false; } } rc = send(socket, new Msg(data[data.length - 1]), 0); return rc >= 0; } public static int sendMsg(SocketBase s, Msg msg, int flags) { int sz = msgSize(msg); boolean rc = s.send(msg, flags); if (!rc) { return -1; } return sz; } // Receiving functions. public static Msg recv(SocketBase s, int flags) { checkSocket(s); return recvMsg(s, flags); } // Receive a multi-part message // // Receives up to *count_ parts of a multi-part message. // Sets *count_ to the actual number of parts read. // ZMQ_RCVMORE is set to indicate if a complete multi-part message was read. // Returns number of message parts read, or -1 on error. // // Note: even if -1 is returned, some parts of the message // may have been read. Therefore the client must consult // *count_ to retrieve message parts successfully read, // even if -1 is returned. // // The iov_base* buffers of each iovec *a_ filled in by this // function may be freed using free(). // // Implementation note: We assume zmq::msg_t buffer allocated // by zmq::recvmsg can be freed by free(). // We assume it is safe to steal these buffers by simply // not closing the zmq::msg_t. // public int recviov(SocketBase s, byte[][] a, int count, int flags) { checkSocket(s); int nread = 0; boolean recvmore = true; for (int i = 0; recvmore && i < count; ++i) { // Cheat! We never close any msg // because we want to steal the buffer. Msg msg = recvMsg(s, flags); if (msg == null) { nread = -1; break; } // Cheat: acquire zmq_msg buffer. a[i] = msg.data(); // Assume zmq_socket ZMQ_RVCMORE is properly set. recvmore = msg.hasMore(); } return nread; } public static Msg recvMsg(SocketBase s, int flags) { return s.recv(flags); } public static boolean join(SocketBase s, String group) { checkSocket(s); return s.join(group); } public static boolean leave(SocketBase s, String group) { checkSocket(s); return s.leave(group); } public static Msg msgInit() { return new Msg(); } public static Msg msgInitWithSize(int messageSize) { return new Msg(messageSize); } public static int msgSize(Msg msg) { return msg.size(); } public static int getMessageOption(Msg msg, int option) { switch (option) { case ZMQ_MORE: return msg.hasMore() ? 1 : 0; default: throw new IllegalArgumentException(); } } // Get message metadata string public static String getMessageMetadata(Msg msg, String property) { String data = null; Metadata metadata = msg.getMetadata(); if (metadata != null) { data = metadata.get(property); } return data; } // Set routing id on a message sent over SERVER socket type public boolean setMessageRoutingId(Msg msg, int routingId) { return msg.setRoutingId(routingId); } // Get the routing id of a message that came from SERVER socket type public int getMessageRoutingId(Msg msg) { return msg.getRoutingId(); } public boolean setMessageGroup(Msg msg, String group) { return msg.setGroup(group); } public String getMessageGroup(Msg msg) { return msg.getGroup(); } public static void sleep(long seconds) { sleep(seconds, TimeUnit.SECONDS); } public static void msleep(long milliseconds) { sleep(milliseconds, TimeUnit.MILLISECONDS); } public static void sleep(long amount, TimeUnit unit) { LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(amount, unit)); } /** * Polling on items with given selector * CAUTION: This could be affected by jdk epoll bug * * @param selector Open and reuse this selector and do not forget to close when it is not used. * @param items * @param timeout * @return number of events */ public static int poll(Selector selector, PollItem[] items, long timeout) { return poll(selector, items, items.length, timeout); } /** * Polling on items with given selector * CAUTION: This could be affected by jdk epoll bug * * @param selector Open and reuse this selector and do not forget to close when it is not used. * @param items * @param count * @param timeout * @return number of events */ public static int poll(Selector selector, PollItem[] items, int count, long timeout) { Utils.checkArgument(items != null, "items have to be supplied for polling"); if (count == 0) { if (timeout <= 0) { return 0; } LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS)); return 0; } long now = 0L; long end = 0L; HashMap saved = new HashMap<>(); for (SelectionKey key : selector.keys()) { if (key.isValid()) { saved.put(key.channel(), key); } } for (int i = 0; i < count; i++) { PollItem item = items[i]; if (item == null) { continue; } SelectableChannel ch = item.getChannel(); // mailbox channel if ZMQ socket SelectionKey key = saved.remove(ch); if (key != null) { if (key.interestOps() != item.interestOps()) { key.interestOps(item.interestOps()); } key.attach(item); } else { try { ch.register(selector, item.interestOps(), item); } catch (ClosedSelectorException e) { // context was closed asynchronously, exit gracefully return -1; } catch (ClosedChannelException e) { throw new ZError.IOException(e); } } } if (!saved.isEmpty()) { for (SelectionKey deprecated : saved.values()) { deprecated.cancel(); } } boolean firstPass = true; int nevents = 0; int ready; while (true) { // Compute the timeout for the subsequent poll. long waitMillis; if (firstPass) { waitMillis = 0L; } else if (timeout < 0L) { waitMillis = -1L; } else { waitMillis = TimeUnit.NANOSECONDS.toMillis(end - now); if (waitMillis == 0) { waitMillis = 1L; } } // Wait for events. try { int rc; if (waitMillis < 0) { rc = selector.select(0); } else if (waitMillis == 0) { rc = selector.selectNow(); } else { rc = selector.select(waitMillis); } for (SelectionKey key : selector.keys()) { PollItem item = (PollItem) key.attachment(); ready = item.readyOps(key, rc); if (ready < 0) { return -1; } if (ready > 0) { nevents++; } } selector.selectedKeys().clear(); } catch (ClosedSelectorException e) { // context was closed asynchronously, exit gracefully return -1; } catch (IOException e) { throw new ZError.IOException(e); } // If timeout is zero, exit immediately whether there are events or not. if (timeout == 0) { break; } if (nevents > 0) { break; } // At this point we are meant to wait for events but there are none. // If timeout is infinite we can just loop until we get some events. if (timeout < 0) { if (firstPass) { firstPass = false; } continue; } // The timeout is finite and there are no events. In the first pass // we get a timestamp of when the polling have begun. (We assume that // first pass have taken negligible time). We also compute the time // when the polling should time out. if (firstPass) { now = Clock.nowNS(); end = now + TimeUnit.MILLISECONDS.toNanos(timeout); if (now == end) { break; } firstPass = false; continue; } // Find out whether timeout have expired. now = Clock.nowNS(); if (now >= end) { break; } } return nevents; } // The proxy functionality public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture) { Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy"); Utils.checkArgument(backend != null, "Backend socket has to be present for proxy"); return Proxy.proxy(frontend, backend, capture, null); } public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control) { Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy"); Utils.checkArgument(backend != null, "Backend socket has to be present for proxy"); return Proxy.proxy(frontend, backend, capture, control); } public static boolean device(int device, SocketBase frontend, SocketBase backend) { Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy"); Utils.checkArgument(backend != null, "Backend socket has to be present for proxy"); return Proxy.proxy(frontend, backend, null, null); } public static long startStopwatch() { return System.nanoTime(); } public static long stopStopwatch(long watch) { return (System.nanoTime() - watch) / 1000; } public static int makeVersion(int major, int minor, int patch) { return ((major) * 10000 + (minor) * 100 + (patch)); } public static String strerror(int errno) { return "Errno = " + errno; } } jeromq-0.6.0/src/main/java/zmq/ZObject.java000066400000000000000000000231201455771126300204710ustar00rootroot00000000000000package zmq; import zmq.io.IEngine; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.pipe.Pipe; import zmq.pipe.YPipeBase; // Base class for all objects that participate in inter-thread // communication. public abstract class ZObject { // Context provides access to the global state. private final Ctx ctx; // Thread ID of the thread the object belongs to. private int tid; protected ZObject(Ctx ctx, int tid) { this.ctx = ctx; this.tid = tid; } protected ZObject(ZObject parent) { this(parent.ctx, parent.tid); } public final int getTid() { return tid; } protected final void setTid(int tid) { this.tid = tid; } public final Ctx getCtx() { return ctx; } @SuppressWarnings("unchecked") final void processCommand(Command cmd) { // System.out.println(Thread.currentThread().getName() + ": Processing command " + cmd); switch (cmd.type) { case ACTIVATE_READ: processActivateRead(); break; case ACTIVATE_WRITE: processActivateWrite((Long) cmd.arg); break; case STOP: processStop(); break; case PLUG: processPlug(); processSeqnum(); break; case OWN: processOwn((Own) cmd.arg); processSeqnum(); break; case ATTACH: processAttach((IEngine) cmd.arg); processSeqnum(); break; case BIND: processBind((Pipe) cmd.arg); processSeqnum(); break; case HICCUP: processHiccup((YPipeBase) cmd.arg); break; case PIPE_TERM: processPipeTerm(); break; case PIPE_TERM_ACK: processPipeTermAck(); break; case TERM_REQ: processTermReq((Own) cmd.arg); break; case TERM: processTerm((Integer) cmd.arg); break; case TERM_ACK: processTermAck(); break; case REAP: processReap((SocketBase) cmd.arg); break; case REAP_ACK: processReapAck(); break; case REAPED: processReaped(); break; case INPROC_CONNECTED: processSeqnum(); break; case CANCEL: processCancel(); break; case DONE: default: throw new IllegalArgumentException(); } } protected final boolean registerEndpoint(String addr, Ctx.Endpoint endpoint) { return ctx.registerEndpoint(addr, endpoint); } protected final boolean unregisterEndpoint(String addr, SocketBase socket) { return ctx.unregisterEndpoint(addr, socket); } protected final void unregisterEndpoints(SocketBase socket) { ctx.unregisterEndpoints(socket); } protected final Ctx.Endpoint findEndpoint(String addr) { return ctx.findEndpoint(addr); } protected final void pendConnection(String addr, Ctx.Endpoint endpoint, Pipe[] pipes) { ctx.pendConnection(addr, endpoint, pipes); } protected final void connectPending(String addr, SocketBase bindSocket) { ctx.connectPending(addr, bindSocket); } protected final void destroySocket(SocketBase socket) { ctx.destroySocket(socket); } // Chooses least loaded I/O thread. protected final IOThread chooseIoThread(long affinity) { return ctx.chooseIoThread(affinity); } protected final void sendStop() { // 'stop' command goes always from administrative thread to // the current object. Command cmd = new Command(this, Command.Type.STOP); ctx.sendCommand(tid, cmd); } protected final void sendPlug(Own destination) { sendPlug(destination, true); } protected final void sendPlug(Own destination, boolean incSeqnum) { if (incSeqnum) { destination.incSeqnum(); } Command cmd = new Command(destination, Command.Type.PLUG); sendCommand(cmd); } protected final void sendOwn(Own destination, Own object) { destination.incSeqnum(); Command cmd = new Command(destination, Command.Type.OWN, object); sendCommand(cmd); } protected final void sendAttach(SessionBase destination, IEngine engine) { sendAttach(destination, engine, true); } protected final void sendAttach(SessionBase destination, IEngine engine, boolean incSeqnum) { if (incSeqnum) { destination.incSeqnum(); } Command cmd = new Command(destination, Command.Type.ATTACH, engine); sendCommand(cmd); } protected final void sendBind(Own destination, Pipe pipe) { sendBind(destination, pipe, true); } protected final void sendBind(Own destination, Pipe pipe, boolean incSeqnum) { if (incSeqnum) { destination.incSeqnum(); } Command cmd = new Command(destination, Command.Type.BIND, pipe); sendCommand(cmd); } protected final void sendActivateRead(Pipe destination) { Command cmd = new Command(destination, Command.Type.ACTIVATE_READ); sendCommand(cmd); } protected final void sendActivateWrite(Pipe destination, long msgsRead) { Command cmd = new Command(destination, Command.Type.ACTIVATE_WRITE, msgsRead); sendCommand(cmd); } protected final void sendHiccup(Pipe destination, YPipeBase pipe) { Command cmd = new Command(destination, Command.Type.HICCUP, pipe); sendCommand(cmd); } protected final void sendPipeTerm(Pipe destination) { Command cmd = new Command(destination, Command.Type.PIPE_TERM); sendCommand(cmd); } protected final void sendPipeTermAck(Pipe destination) { Command cmd = new Command(destination, Command.Type.PIPE_TERM_ACK); sendCommand(cmd); } protected final void sendTermReq(Own destination, Own object) { Command cmd = new Command(destination, Command.Type.TERM_REQ, object); sendCommand(cmd); } protected final void sendTerm(Own destination, int linger) { Command cmd = new Command(destination, Command.Type.TERM, linger); sendCommand(cmd); } protected final void sendTermAck(Own destination) { Command cmd = new Command(destination, Command.Type.TERM_ACK); sendCommand(cmd); } protected final void sendReap(SocketBase socket) { Command cmd = new Command(ctx.getReaper(), Command.Type.REAP, socket); sendCommand(cmd); } protected final void sendReapAck() { Command cmd = new Command(this, Command.Type.REAP_ACK); sendCommand(cmd); } protected final void sendReaped() { Command cmd = new Command(ctx.getReaper(), Command.Type.REAPED); sendCommand(cmd); } protected final void sendInprocConnected(SocketBase socket) { Command cmd = new Command(socket, Command.Type.INPROC_CONNECTED); sendCommand(cmd); } protected final void sendDone() { Command cmd = new Command(null, Command.Type.DONE); ctx.sendCommand(Ctx.TERM_TID, cmd); } protected final void sendCancel() { Command cmd = new Command(this, Command.Type.CANCEL); sendCommand(cmd); } protected void processStop() { throw new UnsupportedOperationException(); } protected void processPlug() { throw new UnsupportedOperationException(); } protected void processOwn(Own object) { throw new UnsupportedOperationException(); } protected void processAttach(IEngine engine) { throw new UnsupportedOperationException(); } protected void processBind(Pipe pipe) { throw new UnsupportedOperationException(); } protected void processActivateRead() { throw new UnsupportedOperationException(); } protected void processActivateWrite(long msgsRead) { throw new UnsupportedOperationException(); } protected void processHiccup(YPipeBase hiccupPipe) { throw new UnsupportedOperationException(); } protected void processPipeTerm() { throw new UnsupportedOperationException(); } protected void processPipeTermAck() { throw new UnsupportedOperationException(); } protected void processTermReq(Own object) { throw new UnsupportedOperationException(); } protected void processTerm(int linger) { throw new UnsupportedOperationException(); } protected void processTermAck() { throw new UnsupportedOperationException(); } protected void processReap(SocketBase socket) { throw new UnsupportedOperationException(); } protected void processReapAck() { } protected void processReaped() { throw new UnsupportedOperationException(); } // Special handler called after a command that requires a seqnum // was processed. The implementation should catch up with its counter // of processed commands here. protected void processSeqnum() { throw new UnsupportedOperationException(); } protected void processCancel() { } private void sendCommand(Command cmd) { ctx.sendCommand(cmd.destination.getTid(), cmd); } } jeromq-0.6.0/src/main/java/zmq/io/000077500000000000000000000000001455771126300166775ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/EngineNotImplemented.java000066400000000000000000000011501455771126300236110ustar00rootroot00000000000000package zmq.io; public class EngineNotImplemented implements IEngine { public EngineNotImplemented() { throw new UnsupportedOperationException(getClass().getName() + " is not implemented"); } @Override public void plug(IOThread ioThread, SessionBase session) { } @Override public void terminate() { } @Override public void restartInput() { } @Override public void restartOutput() { } @Override public void zapMsgAvailable() { } @Override public String getEndPoint() { return null; } } jeromq-0.6.0/src/main/java/zmq/io/HelloMsgSession.java000066400000000000000000000012411455771126300226160ustar00rootroot00000000000000package zmq.io; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.io.net.Address; public class HelloMsgSession extends SessionBase { boolean newPipe; public HelloMsgSession(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { super(ioThread, connect, socket, options, addr); newPipe = true; } @Override protected Msg pullMsg() { if (newPipe) { newPipe = false; return options.helloMsg; } return super.pullMsg(); } @Override protected void reset() { super.reset(); newPipe = true; } } jeromq-0.6.0/src/main/java/zmq/io/IEngine.java000066400000000000000000000012251455771126300210600ustar00rootroot00000000000000package zmq.io; // Abstract interface to be implemented by various engines. public interface IEngine { // Plug the engine to the session. void plug(IOThread ioThread, SessionBase session); // Terminate and deallocate the engine. Note that 'detached' // events are not fired on termination. void terminate(); // This method is called by the session to signal that more // messages can be written to the pipe. void restartInput(); // This method is called by the session to signal that there // are messages to send available. void restartOutput(); void zapMsgAvailable(); String getEndPoint(); } jeromq-0.6.0/src/main/java/zmq/io/IOObject.java000066400000000000000000000053121455771126300212010ustar00rootroot00000000000000package zmq.io; import java.nio.channels.SelectableChannel; import zmq.poll.IPollEvents; import zmq.poll.Poller; import zmq.poll.Poller.Handle; // Simple base class for objects that live in I/O threads. // It makes communication with the poller object easier and // makes defining unneeded event handlers unnecessary. public class IOObject implements IPollEvents { private final Poller poller; private final IPollEvents handler; private boolean alive; public IOObject(IOThread ioThread, IPollEvents handler) { assert (ioThread != null); assert (handler != null); this.handler = handler; // Retrieve the poller from the thread we are running in. poller = ioThread.getPoller(); } // When migrating an object from one I/O thread to another, first // unplug it, then migrate it, then plug it to the new thread. public final void plug() { alive = true; } public final void unplug() { alive = false; } public final Handle addFd(SelectableChannel fd) { return poller.addHandle(fd, this); } public final void removeHandle(Handle handle) { poller.removeHandle(handle); } public final void setPollIn(Handle handle) { poller.setPollIn(handle); } public final void setPollOut(Handle handle) { poller.setPollOut(handle); } public final void setPollConnect(Handle handle) { poller.setPollConnect(handle); } public final void setPollAccept(Handle handle) { poller.setPollAccept(handle); } public final void resetPollIn(Handle handle) { poller.resetPollIn(handle); } public final void resetPollOut(Handle handle) { poller.resetPollOut(handle); } @Override public final void inEvent() { assert (alive); handler.inEvent(); } @Override public final void outEvent() { assert (alive); handler.outEvent(); } @Override public final void connectEvent() { assert (alive); handler.connectEvent(); } @Override public final void acceptEvent() { assert (alive); handler.acceptEvent(); } @Override public final void timerEvent(int id) { assert (alive); handler.timerEvent(id); } public final void addTimer(long timeout, int id) { assert (alive); poller.addTimer(timeout, this, id); } public final void cancelTimer(int id) { assert (alive); poller.cancelTimer(this, id); } @Override public String toString() { return String.valueOf(handler); } } jeromq-0.6.0/src/main/java/zmq/io/IOThread.java000066400000000000000000000037161455771126300212100ustar00rootroot00000000000000package zmq.io; import java.io.Closeable; import java.io.IOException; import java.nio.channels.SelectableChannel; import zmq.Command; import zmq.Ctx; import zmq.Mailbox; import zmq.ZObject; import zmq.poll.IPollEvents; import zmq.poll.Poller; public class IOThread extends ZObject implements IPollEvents, Closeable { // I/O thread accesses incoming commands via this mailbox. private final Mailbox mailbox; // Handle associated with mailbox' file descriptor. private final Poller.Handle mailboxHandle; // I/O multiplexing is performed using a poller object. private final Poller poller; public IOThread(Ctx ctx, int tid) { super(ctx, tid); String name = "iothread-" + tid; poller = new Poller(ctx, name); mailbox = new Mailbox(ctx, name, tid); SelectableChannel fd = mailbox.getFd(); mailboxHandle = poller.addHandle(fd, this); poller.setPollIn(mailboxHandle); } public void start() { poller.start(); } @Override public void close() throws IOException { poller.destroy(); mailbox.close(); } public void stop() { sendStop(); } public Mailbox getMailbox() { return mailbox; } public int getLoad() { return poller.getLoad(); } @Override public void inEvent() { // TODO: Do we want to limit number of commands I/O thread can // process in a single go? while (true) { // Get the next command. If there is none, exit. Command cmd = mailbox.recv(0); if (cmd == null) { break; } // Process the command. cmd.process(); } } Poller getPoller() { assert (poller != null); return poller; } @Override protected void processStop() { poller.removeHandle(mailboxHandle); poller.stop(); } } jeromq-0.6.0/src/main/java/zmq/io/Metadata.java000066400000000000000000000331021455771126300212610ustar00rootroot00000000000000package zmq.io; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import zmq.Msg; import zmq.ZError; import zmq.ZMQ; import zmq.util.Wire; /** * The metadata holder class. *

The class is thread safe, as it uses a {@link ConcurrentHashMap} for the backend

*

Null value are transformed to empty string.

*/ public class Metadata { /** * Call backs during parsing process */ public interface ParseListener { /** * Called when a property has been parsed. * @param name the name of the property. * @param value the value of the property. * @param valueAsString the value in a string representation. * @return 0 to continue the parsing process, any other value to interrupt it. */ int parsed(String name, byte[] value, String valueAsString); } public static final String IDENTITY = "Identity"; public static final String SOCKET_TYPE = "Socket-Type"; public static final String USER_ID = "User-Id"; public static final String PEER_ADDRESS = "Peer-Address"; // Dictionary holding metadata. private final Map dictionary = new ConcurrentHashMap<>(); public Metadata() { super(); } public Metadata(Properties dictionary) { // Ensure a conversion of each entry to a String. // No need to check for nul values as the Properties class ignore null and don't stores them dictionary.forEach((key, value) -> this.dictionary.put(key.toString(), value.toString())); } public Metadata(Map dictionary) { dictionary.forEach((key, value) -> this.dictionary.put(key, Optional.ofNullable(value).orElse(""))); } /** * Returns a {@link Set} view of the keys contained in this metadata. * The set is backed by the metadata, so changes to the map are * reflected in the set, and vice-versa. If the metadata is modified * while an iteration over the set is in progress (except through * the iterator's own {@code remove} operation), the results of * the iteration are undefined. The set supports element removal, * which removes the corresponding mapping from the metadata, via the * {@code Iterator.remove}, {@code Set.remove}, * {@code removeAll}, {@code retainAll}, and {@code clear} * operations. It does not support the {@code add} or {@code addAll} * operations. * * @return a set view of the keys contained in this metadata */ public Set keySet() { return dictionary.keySet(); } /** * Returns a {@link Set} view of the properties contained in this metadata. * The set is backed by the metadata, so changes to the metadata are * reflected in the set, and vice-versa. If the metadata is modified * while an iteration over the set is in progress (except through * the iterator's own {@code remove} operation, or through the * {@code setValue} operation on a metadata property returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the metadata, via the {@code Iterator.remove}, * {@code Set.remove}, {@code removeAll}, {@code retainAll} and * {@code clear} operations. It does not support the * {@code add} or {@code addAll} operations. * * @return a set view of the properties contained in this metadata */ public Set> entrySet() { return dictionary.entrySet(); } /** * Returns a {@link Collection} view of the values contained in this metadata. * The collection is backed by the metadata, so changes to the map are * reflected in the collection, and vice-versa. If the metadata is * modified while an iteration over the collection is in progress * (except through the iterator's own {@code remove} operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * property from the metadata, via the {@code Iterator.remove}, * {@code Collection.remove}, {@code removeAll}, * {@code retainAll} and {@code clear} operations. It does not * support the {@code add} or {@code addAll} operations. * * @return a collection view of the values contained in this map */ public Collection values() { return dictionary.values(); } /** * Removes the property for a name from this metada if it is present. * *

If this map permits null values, then a return value of * {@code null} does not necessarily indicate that the map * contained no mapping for the key; it's also possible that the map * explicitly mapped the key to {@code null}. * *

The map will not contain a mapping for the specified key once the * call returns. * * @param key key whose mapping is to be removed from the map */ public void remove(String key) { dictionary.remove(key); } /** * Returns the value for this property, * or {@code null} if it does not exist. * * @param property the property name * @return the value of the property, or {@code null} if it does not exist. */ public String get(String property) { return dictionary.get(property); } /** * Define the value for this property. If the value is null, an empty string * is stored instead. * * @param property the property name * @param value value to be associated with the specified property * @deprecated Use {@link #put(String, String)} instead */ @Deprecated public void set(String property, String value) { put(property, value); } /** * Define the value for this property. If the value is null, an empty string * is stored instead. * * @param property the property name * @param value value to be associated with the specified property */ public void put(String property, String value) { if (value != null) { dictionary.put(property, value); } else { dictionary.put(property, ""); } } @Override public int hashCode() { return dictionary.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (!(other instanceof Metadata)) { return false; } Metadata that = (Metadata) other; return this.dictionary.equals(that.dictionary); } public void set(Metadata zapProperties) { dictionary.putAll(zapProperties.dictionary); } /** * Returns {@code true} if this map contains no key-value mappings. * * @return {@code true} if this map contains no key-value mappings */ public boolean isEmpty() { return dictionary.isEmpty(); } /** * Returns {@code true} if this metada contains the property requested * * @param property property the name of the property to be tested. * @return {@code true} if this metada contains the property */ public boolean containsKey(String property) { return dictionary.containsKey(property); } /** * Removes all the properties. * The map will be empty after this call returns. */ public void clear() { dictionary.clear(); } /** * Returns the number of properties. If it contains more properties * than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. * * @return the number of properties */ public int size() { return dictionary.size(); } @Override public String toString() { return "Metadata=" + dictionary; } /** * Return the content of the metadata as a new byte array, using the specifications of the ZMTP protocol *

     * property = name value
     * name = OCTET 1*255name-char
     * name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
     * value = 4OCTET *OCTET       ; Size in network byte order
     * 
* @return a new byte array * @throws IllegalStateException if the content can't be serialized */ public byte[] bytes() { try (ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesSize())) { write(stream); return stream.toByteArray(); } catch (IOException e) { throw new IllegalStateException("Unable to write content as bytes", e); } } /** * Return an approximate size of the serialization of the metadata, it will probably be higher if there is a lot * of non ASCII value in it. * @return the size estimation */ private int bytesSize() { int size = 0; for (Entry entry : dictionary.entrySet()) { size += 1; size += entry.getKey().length(); size += 4; size += entry.getValue().length(); } return size; } /** * Serialize metadata to an output stream, using the specifications of the ZMTP protocol *
     * property = name value
     * name = OCTET 1*255name-char
     * name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
     * value = 4OCTET *OCTET       ; Size in network byte order
     * 
* @param stream * @throws IOException if an I/O error occurs. * @throws IllegalStateException if one of the properties name size is bigger than 255 */ public void write(OutputStream stream) throws IOException { for (Entry entry : dictionary.entrySet()) { byte[] keyBytes = entry.getKey().getBytes(ZMQ.CHARSET); if (keyBytes.length > 255) { throw new IllegalStateException("Trying to serialize an oversize attribute name"); } // write the length as a byte stream.write(keyBytes.length); stream.write(keyBytes); byte[] valueBytes = entry.getValue().getBytes(ZMQ.CHARSET); stream.write(Wire.putUInt32(valueBytes.length)); stream.write(valueBytes); } } /** * Deserialize metadata from a {@link Msg}, using the specifications of the ZMTP protocol *
     * property = name value
     * name = OCTET 1*255name-char
     * name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
     * value = 4OCTET *OCTET       ; Size in network byte order
     * 
* @param msg * @param offset * @param listener an optional {@link ParseListener}, can be null. * @return 0 if successful. Otherwise, it returns {@code zmq.ZError.EPROTO} or the error value from the {@link ParseListener}. */ public int read(Msg msg, int offset, ParseListener listener) { return read(msg.buf(), offset, listener); } /** * Deserialize metadata from a {@link ByteBuffer}, using the specifications of the ZMTP protocol *
     * property = name value
     * name = OCTET 1*255name-char
     * name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
     * value = 4OCTET *OCTET       ; Size in network byte order
     * 
* @param msg * @param offset * @param listener an optional {@link ParseListener}, can be null. * @return 0 if successful. Otherwise, it returns {@code zmq.ZError.EPROTO} or the error value from the {@link ParseListener}. */ public int read(ByteBuffer msg, int offset, ParseListener listener) { ByteBuffer data = msg.duplicate(); data.position(offset); int bytesLeft = data.remaining(); int index = offset; while (bytesLeft > 1) { final int nameLength = Byte.toUnsignedInt(data.get(index)); if (nameLength == 0) { break; } index++; bytesLeft -= 1; if (bytesLeft < nameLength) { break; } final String name = new String(bytes(data, index, nameLength), ZMQ.CHARSET); index += nameLength; bytesLeft -= nameLength; if (bytesLeft < 4) { break; } final int valueLength = Wire.getUInt32(data, index); index += 4; bytesLeft -= 4; if (bytesLeft < valueLength || valueLength < 0) { break; } final byte[] value = bytes(data, index, valueLength); final String valueAsString = new String(value, ZMQ.CHARSET); index += valueLength; bytesLeft -= valueLength; if (listener != null) { int rc = listener.parsed(name, value, valueAsString); if (rc != 0) { return rc; } } set(name, valueAsString); } if (bytesLeft > 0) { return ZError.EPROTO; } return 0; } private byte[] bytes(final ByteBuffer buf, final int position, final int length) { final byte[] bytes = new byte[length]; final int current = buf.position(); buf.position(position); buf.get(bytes, 0, length); buf.position(current); return bytes; } } jeromq-0.6.0/src/main/java/zmq/io/Msgs.java000066400000000000000000000022401455771126300204510ustar00rootroot00000000000000package zmq.io; import zmq.Msg; public class Msgs { private Msgs() { // no possible instantiation } /** * Checks if the message starts with the given string. * * @param msg the message to check. * @param data the string to check the message with. Shall be shorter than 256 characters. * @param includeLength true if the string in the message is prefixed with the length, false if not. * @return true if the message starts with the given string, otherwise false. */ public static boolean startsWith(Msg msg, String data, boolean includeLength) { final int length = data.length(); assert (length < 256); int start = includeLength ? 1 : 0; if (msg.size() < length + start) { return false; } boolean comparison = !includeLength || length == (msg.get(0) & 0xff); if (comparison) { for (int idx = start; idx < length; ++idx) { comparison = (msg.get(idx) == data.charAt(idx - start)); if (!comparison) { break; } } } return comparison; } } jeromq-0.6.0/src/main/java/zmq/io/SessionBase.java000066400000000000000000000442111455771126300217620ustar00rootroot00000000000000package zmq.io; import java.util.HashSet; import java.util.Set; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.Own; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.ZObject; import zmq.io.StreamEngine.ErrorReason; import zmq.io.mechanism.Mechanisms; import zmq.io.net.Address; import zmq.io.net.NetProtocol; import zmq.io.net.ipc.IpcConnecter; import zmq.io.net.norm.NormEngine; import zmq.io.net.pgm.PgmReceiver; import zmq.io.net.pgm.PgmSender; import zmq.io.net.tcp.SocksConnecter; import zmq.io.net.tcp.TcpConnecter; import zmq.io.net.tipc.TipcConnecter; import zmq.pipe.Pipe; import zmq.poll.IPollEvents; public class SessionBase extends Own implements Pipe.IPipeEvents, IPollEvents { // If true, this session (re)connects to the peer. Otherwise, it's // a transient session created by the listener. private final boolean active; // Pipe connecting the session to its socket. private Pipe pipe; // Pipe used to exchange messages with ZAP socket. private Pipe zapPipe; // This set is added to with pipes we are disconnecting, but haven't yet completed private final Set terminatingPipes; // This flag is true if the remainder of the message being processed // is still in the in pipe. private boolean incompleteIn; // True if termination have been suspended to push the pending // messages to the network. private boolean pending; // The protocol I/O engine connected to the session. private IEngine engine; // The socket the session belongs to. protected final SocketBase socket; // I/O thread the session is living in. It will be used to plug in // the engines into the same thread. private final IOThread ioThread; // ID of the linger timer private static final int LINGER_TIMER_ID = 0x20; // True is linger timer is running. private boolean hasLingerTimer; // Protocol and address to use when connecting. private final Address addr; private final IOObject ioObject; public SessionBase(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { super(ioThread, options); ioObject = new IOObject(ioThread, this); this.active = connect; pipe = null; zapPipe = null; incompleteIn = false; pending = false; engine = null; this.socket = socket; this.ioThread = ioThread; hasLingerTimer = false; this.addr = addr; terminatingPipes = new HashSet<>(); } @Override public void destroy() { assert (pipe == null); assert (zapPipe == null); // If there's still a pending linger timer, remove it. if (hasLingerTimer) { ioObject.cancelTimer(LINGER_TIMER_ID); hasLingerTimer = false; } // Close the engine. if (engine != null) { engine.terminate(); } ioObject.unplug(); } // To be used once only, when creating the session. public void attachPipe(Pipe pipe) { assert (!isTerminating()); assert (this.pipe == null); assert (pipe != null); this.pipe = pipe; this.pipe.setEventSink(this); } protected Msg pullMsg() { if (pipe == null) { return null; } Msg msg = pipe.read(); if (msg == null) { return null; } incompleteIn = msg.hasMore(); return msg; } protected boolean pushMsg(Msg msg) { if (msg.isCommand()) { return true; } if (pipe != null && pipe.write(msg)) { return true; } errno.set(ZError.EAGAIN); return false; } public Msg readZapMsg() { if (zapPipe == null) { errno.set(ZError.ENOTCONN); return null; } Msg msg = zapPipe.read(); if (msg == null) { errno.set(ZError.EAGAIN); } return msg; } public boolean writeZapMsg(Msg msg) { if (zapPipe == null) { errno.set(ZError.ENOTCONN); return false; } boolean rc = zapPipe.write(msg); assert (rc); if (!msg.hasMore()) { zapPipe.flush(); } return true; } protected void reset() { } public void flush() { if (pipe != null) { pipe.flush(); } } // Remove any half processed messages. Flush unflushed messages. // Call this function when engine disconnect to get rid of leftovers. private void cleanPipes() { assert (pipe != null); // Get rid of half-processed messages in the out pipe. Flush any // unflushed messages upstream. pipe.rollback(); pipe.flush(); // Remove any half-read message from the in pipe. while (incompleteIn) { Msg msg = pullMsg(); if (msg == null) { assert (!incompleteIn); break; } // msg.close (); } } @Override public void pipeTerminated(Pipe pipe) { // Drop the reference to the deallocated pipe. assert (this.pipe == pipe || this.zapPipe == pipe || terminatingPipes.contains(pipe)); if (this.pipe == pipe) { // If this is our current pipe, remove it this.pipe = null; if (hasLingerTimer) { ioObject.cancelTimer(LINGER_TIMER_ID); hasLingerTimer = false; } } else if (zapPipe == pipe) { zapPipe = null; } else { // Remove the pipe from the detached pipes set terminatingPipes.remove(pipe); } if (!isTerminating() && options.rawSocket) { if (engine != null) { engine.terminate(); engine = null; } } // If we are waiting for pending messages to be sent, at this point // we are sure that there will be no more messages and we can proceed // with termination safely. if (pending && this.pipe == null && this.zapPipe == null && terminatingPipes.isEmpty()) { pending = false; super.processTerm(0); } } @Override public void readActivated(Pipe pipe) { // Skip activating if we're detaching this pipe if (this.pipe != pipe && this.zapPipe != pipe) { assert (terminatingPipes.contains(pipe)); return; } if (engine == null) { this.pipe.checkRead(); return; } if (this.pipe == pipe) { engine.restartOutput(); } else { engine.zapMsgAvailable(); } } @Override public void writeActivated(Pipe pipe) { // Skip activating if we're detaching this pipe if (this.pipe != pipe) { assert (terminatingPipes.contains(pipe)); return; } if (engine != null) { engine.restartInput(); } } @Override public void hiccuped(Pipe pipe) { // Hiccups are always sent from session to socket, not the other // way round. throw new UnsupportedOperationException("Must Override"); } public SocketBase getSocket() { return socket; } @Override protected void processPlug() { ioObject.plug(); if (active) { startConnecting(false); } } public int zapConnect() { // Session might be reused with zap connexion already established, don't panic if (zapPipe == null) { Ctx.Endpoint peer = findEndpoint("inproc://zeromq.zap.01"); if (peer.socket == null) { errno.set(ZError.ECONNREFUSED); return ZError.ECONNREFUSED; } if (peer.options.type != ZMQ.ZMQ_REP && peer.options.type != ZMQ.ZMQ_ROUTER && peer.options.type != ZMQ.ZMQ_SERVER) { errno.set(ZError.ECONNREFUSED); return ZError.ECONNREFUSED; } // Create a bi-directional pipe that will connect // session with zap socket. ZObject[] parents = { this, peer.socket }; int[] hwms = { 0, 0 }; boolean[] conflates = { false, false }; Pipe[] pipes = Pipe.pair(parents, hwms, conflates); // Attach local end of the pipe to this socket object. zapPipe = pipes[0]; zapPipe.setNoDelay(); zapPipe.setEventSink(this); sendBind(peer.socket, pipes[1], false); // Send empty identity if required by the peer. if (peer.options.recvIdentity) { Msg id = new Msg(); id.setFlags(Msg.IDENTITY); zapPipe.write(id); zapPipe.flush(); } } return 0; } protected boolean zapEnabled() { return options.mechanism != Mechanisms.NULL || (options.zapDomain != null && !options.zapDomain.isEmpty()); } @Override protected void processAttach(IEngine engine) { assert (engine != null); // Create the pipe if it does not exist yet. if (pipe == null && !isTerminating()) { ZObject[] parents = { this, socket }; boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL || options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB); int[] hwms = { conflate ? -1 : options.recvHwm, conflate ? -1 : options.sendHwm }; boolean[] conflates = { conflate, conflate }; Pipe[] pipes = Pipe.pair(parents, hwms, conflates); // Plug the local end of the pipe. pipes[0].setEventSink(this); // Remember the local end of the pipe. assert (pipe == null); pipe = pipes[0]; // Ask socket to plug into the remote end of the pipe. sendBind(socket, pipes[1]); } // Plug in the engine. assert (this.engine == null); this.engine = engine; this.engine.plug(ioThread, this); } public void engineError(boolean handshaked, ErrorReason reason) { // Engine is dead. Let's forget about it. engine = null; // Remove any half-done messages from the pipes. if (pipe != null) { cleanPipes(); // Only send disconnect message if socket was accepted and handshake was completed if (!active && handshaked && options.canReceiveDisconnectMsg && options.disconnectMsg != null) { pipe.setDisconnectMsg(options.disconnectMsg); pipe.sendDisconnectMsg(); } if (active && handshaked && options.canReceiveHiccupMsg && options.hiccupMsg != null) { pipe.sendHiccupMsg(options.hiccupMsg); } } assert (reason == ErrorReason.CONNECTION || reason == ErrorReason.TIMEOUT || reason == ErrorReason.PROTOCOL); switch (reason) { case TIMEOUT: case CONNECTION: if (active) { reconnect(); } else { terminate(); } break; case PROTOCOL: terminate(); break; default: break; } // Just in case there's only a delimiter in the pipe. if (pipe != null) { pipe.checkRead(); } if (zapPipe != null) { zapPipe.checkRead(); } } @Override protected void processTerm(int linger) { assert (!pending); // If the termination of the pipe happens before the term command is // delivered there's nothing much to do. We can proceed with the // standard termination immediately. if (pipe == null && zapPipe == null && terminatingPipes.isEmpty()) { super.processTerm(0); return; } pending = true; if (pipe != null) { // If there's finite linger value, delay the termination. // If linger is infinite (negative) we don't even have to set // the timer. if (linger > 0) { assert (!hasLingerTimer); ioObject.addTimer(linger, LINGER_TIMER_ID); hasLingerTimer = true; } // Start pipe termination process. Delay the termination till all messages // are processed in case the linger time is non-zero. pipe.terminate(linger != 0); // TODO: Should this go into pipe_t::terminate ? // In case there's no engine and there's only delimiter in the // pipe it wouldn't be ever read. Thus we check for it explicitly. if (engine == null) { pipe.checkRead(); } } if (zapPipe != null) { zapPipe.terminate(false); } } @Override public void timerEvent(int id) { // Linger period expired. We can proceed with termination even though // there are still pending messages to be sent. assert (id == LINGER_TIMER_ID); hasLingerTimer = false; // Ask pipe to terminate even though there may be pending messages in it. assert (pipe != null); pipe.terminate(false); } private void reconnect() { // TODO V4 - // Transient session self-destructs after peer disconnects. ? // For delayed connect situations, terminate the pipe // and reestablish later on if (pipe != null && !options.immediate && !addr.protocol().isMulticast) { pipe.hiccup(); pipe.terminate(false); terminatingPipes.add(pipe); pipe = null; } reset(); // Reconnect. if (options.reconnectIvl != -1) { startConnecting(true); } // For subscriber sockets we hiccup the inbound pipe, which will cause // the socket object to resend all the subscriptions. if (pipe != null && (options.type == ZMQ.ZMQ_SUB || options.type == ZMQ.ZMQ_XSUB)) { pipe.hiccup(); } } private void startConnecting(boolean wait) { assert (active); // Choose I/O thread to run connecter in. Given that we are already // running in an I/O thread, there must be at least one available. IOThread ioThread = chooseIoThread(options.affinity); assert (ioThread != null); // Create the connecter object. NetProtocol protocol = addr.protocol(); if (protocol == null) { errno.set(ZError.EPROTONOSUPPORT); return; } switch (protocol) { case tcp: if (options.socksProxyAddress != null) { Address proxyAddress = new Address(NetProtocol.tcp, options.socksProxyAddress); SocksConnecter connecter = new SocksConnecter(ioThread, this, options, addr, proxyAddress, wait); launchChild(connecter); } else { TcpConnecter connecter = new TcpConnecter(ioThread, this, options, addr, wait); launchChild(connecter); } break; case ipc: { IpcConnecter connecter = new IpcConnecter(ioThread, this, options, addr, wait); launchChild(connecter); } break; case tipc: { TipcConnecter connecter = new TipcConnecter(ioThread, this, options, addr, wait); launchChild(connecter); } break; case pgm: case epgm: { assert (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB || options.type == ZMQ.ZMQ_SUB || options.type == ZMQ.ZMQ_XSUB); // For EPGM transport with UDP encapsulation of PGM is used. boolean udpEncapsulation = protocol == NetProtocol.epgm; // At this point we'll create message pipes to the session straight // away. There's no point in delaying it as no concept of 'connect' // exists with PGM anyway. if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) { // PGM sender. PgmSender pgmSender = new PgmSender(ioThread, options); boolean rc = pgmSender.init(udpEncapsulation, addr); assert (rc); sendAttach(this, pgmSender); } else { // PGM receiver. PgmReceiver pgmReceiver = new PgmReceiver(ioThread, options); boolean rc = pgmReceiver.init(udpEncapsulation, addr); assert (rc); sendAttach(this, pgmReceiver); } } break; case norm: { // At this point we'll create message pipes to the session straight // away. There's no point in delaying it as no concept of 'connect' // exists with NORM anyway. if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) { // NORM sender. NormEngine normSender = new NormEngine(ioThread, options); boolean rc = normSender.init(addr, true, false); assert (rc); sendAttach(this, normSender); } else { // NORM receiver. NormEngine normReceiver = new NormEngine(ioThread, options); boolean rc = normReceiver.init(addr, false, true); assert (rc); sendAttach(this, normReceiver); } } break; default: assert (false); break; } } public String getEndpoint() { return engine.getEndPoint(); } @Override public String toString() { return getClass().getSimpleName() + "-" + socket; } @Override public final void incSeqnum() { super.incSeqnum(); } } jeromq-0.6.0/src/main/java/zmq/io/StreamEngine.java000066400000000000000000001203751455771126300221330ustar00rootroot00000000000000package zmq.io; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Arrays; import zmq.Config; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.coder.IDecoder; import zmq.io.coder.IDecoder.Step; import zmq.io.coder.IEncoder; import zmq.io.coder.raw.RawDecoder; import zmq.io.coder.raw.RawEncoder; import zmq.io.coder.v1.V1Decoder; import zmq.io.coder.v1.V1Encoder; import zmq.io.coder.v2.V2Decoder; import zmq.io.coder.v2.V2Encoder; import zmq.io.mechanism.Mechanism; import zmq.io.mechanism.Mechanisms; import zmq.io.net.Address; import zmq.poll.IPollEvents; import zmq.poll.Poller; import zmq.util.Blob; import zmq.util.Errno; import zmq.util.Utils; import zmq.util.ValueReference; import zmq.util.Wire; import zmq.util.function.Function; import zmq.util.function.Supplier; // This engine handles any socket with SOCK_STREAM semantics, // e.g. TCP socket or an UNIX domain socket. public class StreamEngine implements IEngine, IPollEvents { private final class ProducePongMessage implements Supplier { private final byte[] pingContext; public ProducePongMessage(byte[] pingContext) { assert (pingContext != null); this.pingContext = pingContext; } @Override public Msg get() { return producePongMessage(pingContext); } } // Protocol revisions private enum Protocol { V0(-1), V1(0), V2(1), V3(3); private final byte revision; Protocol(int revision) { this.revision = (byte) revision; } } public enum ErrorReason { PROTOCOL, CONNECTION, TIMEOUT, } private IOObject ioObject; // Underlying socket. private SocketChannel fd; private Poller.Handle handle; private ByteBuffer inpos; private int insize; private IDecoder decoder; private final ValueReference outpos; private int outsize; private IEncoder encoder; private Metadata metadata; // When true, we are still trying to determine whether // the peer is using versioned protocol, and if so, which // version. When false, normal message flow has started. private boolean handshaking; private static final int SIGNATURE_SIZE = 10; // Size of ZMTP/1.0 and ZMTP/2.0 greeting message private static final int V2_GREETING_SIZE = 12; // Size of ZMTP/3.0 greeting message private static final int V3_GREETING_SIZE = 64; // Expected greeting size. private int greetingSize; // Greeting received from, and sent to peer private final ByteBuffer greetingRecv; private final ByteBuffer greetingSend; // handy reminder of the used ZMTP protocol version private Protocol zmtpVersion; // The session this engine is attached to. private SessionBase session; private final Options options; // String representation of endpoint private final String endpoint; private boolean plugged; private Supplier nextMsg; private Function processMsg; private boolean ioError; // Indicates whether the engine is to inject a phantom // subscription message into the incoming stream. // Needed to support old peers. private boolean subscriptionRequired; private Mechanism mechanism; // True if the engine couldn't consume the last decoded message. private boolean inputStopped; // True if the engine doesn't have any message to encode. private boolean outputStopped; // ID of the handshake timer private static final int HANDSHAKE_TIMER_ID = 0x40; private static final int HEARTBEAT_TTL_TIMER_ID = 0x80; private static final int HEARTBEAT_IVL_TIMER_ID = 0x81; private static final int HEARTBEAT_TIMEOUT_TIMER_ID = 0x82; // True is linger timer is running. private boolean hasHandshakeTimer; private boolean hasTtlTimer; private boolean hasTimeoutTimer; private boolean hasHeartbeatTimer; private final int heartbeatTimeout; private final byte[] heartbeatContext; // Socket private SocketBase socket; private final Address peerAddress; private final Address selfAddress; private final Errno errno; public StreamEngine(SocketChannel fd, final Options options, final String endpoint) { this.errno = options.errno; this.fd = fd; this.handshaking = true; greetingSize = V2_GREETING_SIZE; this.options = options; this.endpoint = endpoint; Supplier nextIdentity = this::identityMsg; nextMsg = nextIdentity; processMsg = processIdentity; outpos = new ValueReference<>(); greetingRecv = ByteBuffer.allocate(V3_GREETING_SIZE); greetingSend = ByteBuffer.allocate(V3_GREETING_SIZE); // Put the socket into non-blocking mode. try { Utils.unblockSocket(this.fd); } catch (IOException e) { throw new ZError.IOException(e); } peerAddress = Utils.getPeerIpAddress(fd); selfAddress = Utils.getLocalIpAddress(fd); heartbeatTimeout = heartbeatTimeout(); heartbeatContext = Arrays.copyOf(options.heartbeatContext, options.heartbeatContext.length); } private int heartbeatTimeout() { int timeout = 0; if (options.heartbeatInterval > 0) { timeout = options.heartbeatTimeout; if (timeout == -1) { timeout = options.heartbeatInterval; } } return timeout; } public void destroy() { assert (!plugged); if (fd != null) { try { fd.close(); } catch (IOException e) { assert (false); } fd = null; } if (encoder != null) { encoder.destroy(); } if (decoder != null) { decoder.destroy(); } if (mechanism != null) { mechanism.destroy(); } } @Override public void plug(IOThread ioThread, SessionBase session) { assert (!plugged); plugged = true; // Connect to session object. assert (this.session == null); assert (session != null); this.session = session; socket = session.getSocket(); // Connect to I/O threads poller object. ioObject = new IOObject(ioThread, this); ioObject.plug(); handle = ioObject.addFd(fd); ioError = false; // Make sure batch sizes match large buffer sizes final int inBatchSize = Math.max(options.rcvbuf, Config.IN_BATCH_SIZE.getValue()); final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue()); if (options.rawSocket) { decoder = instantiate(options.decoder, inBatchSize, options.maxMsgSize); if (decoder == null) { decoder = new RawDecoder(inBatchSize); } encoder = instantiate(options.encoder, outBatchSize, options.maxMsgSize); if (encoder == null) { encoder = new RawEncoder(errno, outBatchSize); } // disable handshaking for raw socket handshaking = false; nextMsg = pullMsgFromSession; processMsg = pushRawMsgToSession; if (peerAddress != null && !peerAddress.address().isEmpty()) { assert (metadata == null); // Compile metadata metadata = new Metadata(); metadata.put(Metadata.PEER_ADDRESS, peerAddress.address()); } if (options.selfAddressPropertyName != null && ! options.selfAddressPropertyName.isEmpty() && selfAddress != null && !selfAddress.address().isEmpty()) { if (metadata == null) { metadata = new Metadata(); } metadata.put(options.selfAddressPropertyName, selfAddress.address()); } // For raw sockets, send an initial 0-length message to the // application so that it knows a peer has connected. Msg connector = new Msg(); pushRawMsgToSession(connector); session.flush(); } else { // start optional timer, to prevent handshake hanging on no input setHandshakeTimer(); // Send the 'length' and 'flags' fields of the identity message. // The 'length' field is encoded in the long format. greetingSend.put((byte) 0xff); Wire.putUInt64(greetingSend, options.identitySize + 1); greetingSend.put((byte) 0x7f); outpos.set(greetingSend); outsize = greetingSend.position(); greetingSend.flip(); } ioObject.setPollIn(handle); ioObject.setPollOut(handle); // Flush all the data that may have been already received downstream. inEvent(); } private T instantiate(Class clazz, int size, long max) { if (clazz == null) { return null; } try { return clazz.getConstructor(int.class, long.class).newInstance(size, max); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { throw new ZError.InstantiationException(e); } } private void unplug() { assert (plugged); plugged = false; // Cancel all timers. if (hasHandshakeTimer) { ioObject.cancelTimer(HANDSHAKE_TIMER_ID); hasHandshakeTimer = false; } if (hasTtlTimer) { ioObject.cancelTimer(HEARTBEAT_TTL_TIMER_ID); hasTtlTimer = false; } if (hasTimeoutTimer) { ioObject.cancelTimer(HEARTBEAT_TIMEOUT_TIMER_ID); hasTimeoutTimer = false; } if (hasHeartbeatTimer) { ioObject.cancelTimer(HEARTBEAT_IVL_TIMER_ID); hasHeartbeatTimer = false; } if (!ioError) { // Cancel all fd subscriptions. ioObject.removeHandle(handle); handle = null; } // Disconnect from I/O threads poller object. ioObject.unplug(); session = null; } @Override public void terminate() { unplug(); destroy(); } @Override public void inEvent() { assert (!ioError); // If still handshaking, receive and process the greeting message. if (handshaking) { if (!handshake()) { return; } } assert (decoder != null); // If there has been an I/O error, stop polling. if (inputStopped) { ioObject.removeHandle(handle); handle = null; ioError = true; return; } // If there's no data to process in the buffer... if (insize == 0) { // Retrieve the buffer and read as much data as possible. // Note that buffer can be arbitrarily large. However, we assume // the underlying TCP layer has fixed buffer size and thus the // number of bytes read will be always limited. inpos = decoder.getBuffer(); int rc = read(inpos); if (rc == 0) { error(ErrorReason.CONNECTION); } if (rc == -1) { if (!errno.is(ZError.EAGAIN)) { error(ErrorReason.CONNECTION); } return; } // Adjust input size inpos.flip(); insize = rc; } boolean rc = false; ValueReference processed = new ValueReference<>(0); while (insize > 0) { // Push the data to the decoder. Step.Result result = decoder.decode(inpos, insize, processed); assert (processed.get() <= insize); insize -= processed.get(); if (result == Step.Result.MORE_DATA) { rc = true; break; } if (result == Step.Result.ERROR) { rc = false; break; } Msg msg = decoder.msg(); rc = processMsg.apply(msg); if (!rc) { break; } } // Tear down the connection if we have failed to decode input data // or the session has rejected the message. if (!rc) { if (!errno.is(ZError.EAGAIN)) { error(ErrorReason.PROTOCOL); return; } inputStopped = true; ioObject.resetPollIn(handle); } // Flush all messages the decoder may have produced. session.flush(); } @Override public void outEvent() { assert (!ioError); // If write buffer is empty, try to read new data from the encoder. if (outsize == 0) { // Even when we stop polling as soon as there is no // data to send, the poller may invoke outEvent one // more time due to 'speculative write' optimization. if (encoder == null) { assert (handshaking); return; } outpos.set(null); outsize = encoder.encode(outpos, 0); // Make sure batch sizes match large buffer sizes final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue()); while (outsize < outBatchSize) { Msg msg = nextMsg.get(); if (msg == null) { break; } encoder.loadMsg(msg); int n = encoder.encode(outpos, outBatchSize - outsize); assert (n > 0); outsize += n; } // If there is no data to send, stop polling for output. if (outsize == 0) { outputStopped = true; ioObject.resetPollOut(handle); return; } // slight difference with libzmq: // encoder is notified of the end of the loading encoder.encoded(); } // If there are any data to write in write buffer, write as much as // possible to the socket. Note that amount of data to write can be // arbitrarily large. However, we assume that underlying TCP layer has // limited transmission buffer and thus the actual number of bytes // written should be reasonably modest. int nbytes = write(outpos.get()); // IO error has occurred. We stop waiting for output events. // The engine is not terminated until we detect input error; // this is necessary to prevent losing incoming messages. if (nbytes == -1) { ioObject.resetPollOut(handle); return; } outsize -= nbytes; // If we are still handshaking and there are no data // to send, stop polling for output. if (handshaking) { if (outsize == 0) { ioObject.resetPollOut(handle); } } } @Override public void restartOutput() { if (ioError) { return; } if (outputStopped) { ioObject.setPollOut(handle); outputStopped = false; } // Speculative write: The assumption is that at the moment new message // was sent by the user the socket is probably available for writing. // Thus we try to write the data to socket avoiding polling for POLLOUT. // Consequently, the latency should be better in request/reply scenarios. outEvent(); } @Override public void restartInput() { assert (inputStopped); assert (session != null); assert (decoder != null); Msg msg = decoder.msg(); if (!processMsg.apply(msg)) { if (errno.is(ZError.EAGAIN)) { session.flush(); } else { error(ErrorReason.PROTOCOL); } return; } boolean decodingSuccess = decodeCurrentInputs(); if (!decodingSuccess && errno.is(ZError.EAGAIN)) { session.flush(); } else if (ioError) { error(ErrorReason.CONNECTION); } else if (!decodingSuccess) { error(ErrorReason.PROTOCOL); } else { inputStopped = false; ioObject.setPollIn(handle); session.flush(); // Speculative read. inEvent(); } } private boolean decodeCurrentInputs() { while (insize > 0) { ValueReference processed = new ValueReference<>(0); Step.Result result = decoder.decode(inpos, insize, processed); assert (processed.get() <= insize); insize -= processed.get(); if (result == Step.Result.MORE_DATA) { return true; } if (result == Step.Result.ERROR) { return false; } if (!processMsg.apply(decoder.msg())) { return false; } } return true; } // Detects the protocol used by the peer. private boolean handshake() { assert (handshaking); assert (greetingRecv.position() < greetingSize); final Mechanisms mechanism = options.mechanism; assert (mechanism != null); // Position of the version field in the greeting. final int revisionPos = SIGNATURE_SIZE; // Make sure batch sizes match large buffer sizes final int inBatchSize = Math.max(options.rcvbuf, Config.IN_BATCH_SIZE.getValue()); final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue()); // Receive the greeting. while (greetingRecv.position() < greetingSize) { final int n = read(greetingRecv); if (n == 0) { error(ErrorReason.CONNECTION); return false; } if (n == -1) { if (!errno.is(ZError.EAGAIN)) { error(ErrorReason.CONNECTION); } return false; } // We have received at least one byte from the peer. // If the first byte is not 0xff, we know that the // peer is using unversioned protocol. if ((greetingRecv.get(0) & 0xff) != 0xff) { // If this first byte is not %FF, // then the other peer is using ZMTP 1.0. break; } if (greetingRecv.position() < SIGNATURE_SIZE) { continue; } // Inspect the right-most bit of the 10th byte (which coincides // with the 'flags' field if a regular message was sent). // Zero indicates this is a header of identity message // (i.e. the peer is using the unversioned protocol). if ((greetingRecv.get(9) & 0x01) != 0x01) { break; } // If the least significant bit is 1, the peer is using ZMTP 2.0 or later // and has sent us the ZMTP signature. int outpos = greetingSend.position(); // The peer is using versioned protocol. // Send the major version number. if (greetingSend.limit() == SIGNATURE_SIZE) { if (outsize == 0) { ioObject.setPollOut(handle); } greetingSend.limit(SIGNATURE_SIZE + 1); greetingSend.put(revisionPos, Protocol.V3.revision); // Major version number outsize += 1; } if (greetingRecv.position() > SIGNATURE_SIZE) { if (greetingSend.limit() == SIGNATURE_SIZE + 1) { if (outsize == 0) { ioObject.setPollOut(handle); } // We read a further byte, which indicates the ZMTP version. byte protocol = greetingRecv.get(revisionPos); if (protocol == Protocol.V1.revision || protocol == Protocol.V2.revision) { // If this is V1 or V2, we have a ZMTP 2.0 peer. greetingSend.limit(V2_GREETING_SIZE); greetingSend.position(SIGNATURE_SIZE + 1); greetingSend.put((byte) options.type); // Socket type outsize += 1; } else { // If this is 3 or greater, we have a ZMTP 3.0 peer. greetingSend.limit(V3_GREETING_SIZE); greetingSend.position(SIGNATURE_SIZE + 1); greetingSend.put((byte) 0); // Minor version number outsize += 1; greetingSend.mark(); greetingSend.put(new byte[20]); assert (mechanism == Mechanisms.NULL || mechanism == Mechanisms.PLAIN || mechanism == Mechanisms.CURVE || mechanism == Mechanisms.GSSAPI); greetingSend.reset(); greetingSend.put(mechanism.name().getBytes(ZMQ.CHARSET)); greetingSend.reset(); greetingSend.position(greetingSend.position() + 20); outsize += 20; greetingSend.put(new byte[32]); outsize += 32; greetingSize = V3_GREETING_SIZE; } } } greetingSend.position(outpos); } // Is the peer using the unversioned protocol? // If so, we send and receive rest of identity // messages. if ((greetingRecv.get(0) & 0xff) != 0xff || (greetingRecv.get(9) & 0x01) == 0) { // If this first byte is %FF, then we read nine further bytes, // and inspect the last byte (the 10th in total). // If the least significant bit is 0, then the other peer is using ZMTP 1.0. if (session.zapEnabled()) { // reject ZMTP 1.0 connections if ZAP is enabled error(ErrorReason.PROTOCOL); return false; } zmtpVersion = Protocol.V0; encoder = new V1Encoder(errno, outBatchSize); decoder = new V1Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator); // We have already sent the message header. // Since there is no way to tell the encoder to // skip the message header, we simply throw that // header data away. final int headerSize = options.identitySize + 1 >= 255 ? 10 : 2; ByteBuffer tmp = ByteBuffer.allocate(headerSize); // Prepare the identity message and load it into encoder. // Then consume bytes we have already sent to the peer. Msg txMsg = new Msg(options.identitySize); txMsg.put(options.identity, 0, options.identitySize); encoder.loadMsg(txMsg); ValueReference bufferp = new ValueReference<>(tmp); int bufferSize = encoder.encode(bufferp, headerSize); assert (bufferSize == headerSize); // Make sure the decoder sees the data we have already received. decodeDataAfterHandshake(0); // To allow for interoperability with peers that do not forward // their subscriptions, we inject a phantom subscription message // message into the incoming message stream. if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) { subscriptionRequired = true; } // We are sending our identity now and the next message // will come from the socket. nextMsg = pullMsgFromSession; // We are expecting identity message. processMsg = processIdentity; } else if (greetingRecv.get(revisionPos) == Protocol.V1.revision) { // ZMTP/1.0 framing. zmtpVersion = Protocol.V1; if (session.zapEnabled()) { // reject ZMTP 1.0 connections if ZAP is enabled error(ErrorReason.PROTOCOL); return false; } encoder = new V1Encoder(errno, outBatchSize); decoder = new V1Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator); decodeDataAfterHandshake(V2_GREETING_SIZE); } else if (greetingRecv.get(revisionPos) == Protocol.V2.revision) { // ZMTP/2.0 framing. zmtpVersion = Protocol.V2; if (session.zapEnabled()) { // reject ZMTP 2.0 connections if ZAP is enabled error(ErrorReason.PROTOCOL); return false; } encoder = new V2Encoder(errno, outBatchSize); decoder = new V2Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator); decodeDataAfterHandshake(V2_GREETING_SIZE); } else { zmtpVersion = Protocol.V3; encoder = new V2Encoder(errno, outBatchSize); decoder = new V2Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator); greetingRecv.position(V2_GREETING_SIZE); if (mechanism.isMechanism(greetingRecv)) { this.mechanism = mechanism.create(session, peerAddress, options); } else { error(ErrorReason.PROTOCOL); return false; } nextMsg = nextHandshakeCommand; processMsg = processHandshakeCommand; } // Start polling for output if necessary. if (outsize == 0) { ioObject.setPollOut(handle); } // Handshaking was successful. // Switch into the normal message flow. handshaking = false; if (hasHandshakeTimer) { ioObject.cancelTimer(HANDSHAKE_TIMER_ID); hasHandshakeTimer = false; } socket.eventHandshaken(endpoint, zmtpVersion.ordinal()); return true; } private void decodeDataAfterHandshake(int greetingSize) { final int pos = greetingRecv.position(); if (pos > greetingSize) { // data is present after handshake greetingRecv.position(greetingSize).limit(pos); // Make sure the decoder sees this extra data. inpos = greetingRecv; insize = greetingRecv.remaining(); } } private Msg identityMsg() { Msg msg = new Msg(options.identitySize); if (options.identitySize > 0) { msg.put(options.identity, 0, options.identitySize); } nextMsg = pullMsgFromSession; return msg; } private boolean processIdentityMsg(Msg msg) { if (options.recvIdentity) { msg.setFlags(Msg.IDENTITY); boolean rc = session.pushMsg(msg); assert (rc); } if (subscriptionRequired) { // Inject the subscription message, so that also // ZMQ 2.x peers receive published messages. Msg subscription = new Msg(1); subscription.put((byte) 1); boolean rc = session.pushMsg(subscription); assert (rc); } processMsg = pushMsgToSession; return true; } private final Function processIdentity = this::processIdentityMsg; private Msg nextHandshakeCommand() { assert (mechanism != null); if (mechanism.status() == Mechanism.Status.READY) { mechanismReady(); return pullAndEncode.get(); } else if (mechanism.status() == Mechanism.Status.ERROR) { errno.set(ZError.EPROTO); // error(ErrorReason.PROTOCOL); return null; } else { Msg.Builder msg = new Msg.Builder(); int rc = mechanism.nextHandshakeCommand(msg); if (rc == 0) { msg.setFlags(Msg.COMMAND); return msg.build(); } else { errno.set(rc); return null; } } } private boolean processHandshakeCommand(Msg msg) { assert (mechanism != null); int rc = mechanism.processHandshakeCommand(msg); if (rc == 0) { if (mechanism.status() == Mechanism.Status.READY) { mechanismReady(); } else if (mechanism.status() == Mechanism.Status.ERROR) { errno.set(ZError.EPROTO); return false; } if (outputStopped) { restartOutput(); } } else { errno.set(rc); } return rc == 0; } private final Function processHandshakeCommand = this::processHandshakeCommand; private final Supplier nextHandshakeCommand = this::nextHandshakeCommand; @Override public void zapMsgAvailable() { assert (mechanism != null); int rc = mechanism.zapMsgAvailable(); if (rc == -1) { error(ErrorReason.PROTOCOL); return; } if (inputStopped) { restartInput(); } if (outputStopped) { restartOutput(); } } private void mechanismReady() { if (options.heartbeatInterval > 0) { ioObject.addTimer(options.heartbeatInterval, HEARTBEAT_IVL_TIMER_ID); hasHeartbeatTimer = true; } if (options.recvIdentity) { Msg identity = mechanism.peerIdentity(); boolean rc = session.pushMsg(identity); if (!rc && errno.is(ZError.EAGAIN)) { // If the write is failing at this stage with // an EAGAIN the pipe must be being shut down, // so we can just bail out of the identity set. return; } assert (rc); session.flush(); } nextMsg = pullAndEncode; processMsg = writeCredential; // Compile metadata. assert (metadata == null); metadata = new Metadata(); // If we have a peer_address, add it to metadata if (peerAddress != null && !peerAddress.address().isEmpty()) { metadata.set(Metadata.PEER_ADDRESS, peerAddress.address()); } // If we have a local_address, add it to metadata if (options.selfAddressPropertyName != null && ! options.selfAddressPropertyName.isEmpty() && selfAddress != null && !selfAddress.address().isEmpty()) { metadata.put(options.selfAddressPropertyName, selfAddress.address()); } // Add ZAP properties. metadata.set(mechanism.zapProperties); // Add ZMTP properties. metadata.set(mechanism.zmtpProperties); if (metadata.isEmpty()) { metadata = null; } } private Msg pullMsgFromSession() { return session.pullMsg(); } private boolean pushMsgToSession(Msg msg) { return session.pushMsg(msg); } private final Function pushMsgToSession = this::pushMsgToSession; private final Supplier pullMsgFromSession = this::pullMsgFromSession; private boolean pushRawMsgToSession(Msg msg) { if (metadata != null && !metadata.equals(msg.getMetadata())) { msg.setMetadata(metadata); } return pushMsgToSession(msg); } private final Function pushRawMsgToSession = this::pushRawMsgToSession; private boolean writeCredential(Msg msg) { assert (mechanism != null); assert (session != null); Blob credential = mechanism.getUserId(); if (credential != null && credential.size() > 0) { Msg cred = new Msg(credential.size()); cred.put(credential.data(), 0, credential.size()); cred.setFlags(Msg.CREDENTIAL); boolean rc = session.pushMsg(cred); if (!rc) { return false; } } processMsg = decodeAndPush; return decodeAndPush.apply(msg); } private final Function writeCredential = this::writeCredential; private Msg pullAndEncode() { assert (mechanism != null); Msg msg = session.pullMsg(); if (msg == null) { return null; } msg = mechanism.encode(msg); return msg; } private final Supplier pullAndEncode = this::pullAndEncode; private boolean decodeAndPush(Msg msg) { assert (mechanism != null); msg = mechanism.decode(msg); if (msg == null) { return false; } if (hasTimeoutTimer) { hasTimeoutTimer = false; ioObject.cancelTimer(HEARTBEAT_TIMEOUT_TIMER_ID); } if (hasTtlTimer) { hasTtlTimer = false; ioObject.cancelTimer(HEARTBEAT_TTL_TIMER_ID); } if (msg.isCommand()) { StreamEngine.this.processCommand(msg); } if (metadata != null) { msg.setMetadata(metadata); } boolean rc = session.pushMsg(msg); if (!rc) { if (errno.is(ZError.EAGAIN)) { processMsg = pushOneThenDecodeAndPush; } return false; } return true; } private final Function decodeAndPush = this::decodeAndPush; private boolean pushOneThenDecodeAndPush(Msg msg) { boolean rc = session.pushMsg(msg); if (rc) { processMsg = decodeAndPush; } return rc; } private final Function pushOneThenDecodeAndPush = this::pushOneThenDecodeAndPush; private final Supplier producePingMessage = this::producePingMessage; // Function to handle network disconnections. private void error(ErrorReason error) { if (options.rawSocket) { // For raw sockets, send a final 0-length message to the application // so that it knows the peer has been disconnected. Msg terminator = new Msg(); processMsg.apply(terminator); } assert (session != null); socket.eventDisconnected(endpoint, fd); session.flush(); session.engineError(!handshaking && (mechanism == null || mechanism.status() != Mechanism.Status.HANDSHAKING), error); unplug(); destroy(); } private void setHandshakeTimer() { assert (!hasHandshakeTimer); if (!options.rawSocket && options.handshakeIvl > 0) { ioObject.addTimer(options.handshakeIvl, HANDSHAKE_TIMER_ID); hasHandshakeTimer = true; } } @Override public void timerEvent(int id) { if (id == HANDSHAKE_TIMER_ID) { hasHandshakeTimer = false; // handshake timer expired before handshake completed, so engine fails error(ErrorReason.TIMEOUT); } else if (id == HEARTBEAT_IVL_TIMER_ID) { nextMsg = producePingMessage; outEvent(); ioObject.addTimer(options.heartbeatInterval, HEARTBEAT_IVL_TIMER_ID); } else if (id == HEARTBEAT_TTL_TIMER_ID) { hasTtlTimer = false; error(ErrorReason.TIMEOUT); } else if (id == HEARTBEAT_TIMEOUT_TIMER_ID) { hasTimeoutTimer = false; error(ErrorReason.TIMEOUT); } else { // There are no other valid timer ids! assert (false); } } private Msg producePingMessage() { assert (mechanism != null); Msg msg = new Msg(7 + heartbeatContext.length); msg.setFlags(Msg.COMMAND); msg.putShortString("PING"); Wire.putUInt16(msg, options.heartbeatTtl); msg.put(heartbeatContext); msg = mechanism.encode(msg); nextMsg = pullAndEncode; if (!hasTimeoutTimer && heartbeatTimeout > 0) { ioObject.addTimer(heartbeatTimeout, HEARTBEAT_TIMEOUT_TIMER_ID); hasTimeoutTimer = true; } return msg; } private Msg producePongMessage(byte[] pingContext) { assert (mechanism != null); assert (pingContext != null); Msg msg = new Msg(5 + pingContext.length); msg.setFlags(Msg.COMMAND); msg.putShortString("PONG"); msg.put(pingContext); msg = mechanism.encode(msg); nextMsg = pullAndEncode; return msg; } private boolean processCommand(Msg msg) { if (Msgs.startsWith(msg, "PING", true)) { return processHeartbeatMessage(msg); } return false; } private boolean processHeartbeatMessage(Msg msg) { // Get the remote heartbeat TTL to setup the timer int remoteHeartbeatTtl = msg.getShort(5); // The remote heartbeat is in 10ths of a second // so we multiply it by 100 to get the timer interval in ms. remoteHeartbeatTtl *= 100; if (!hasTtlTimer && remoteHeartbeatTtl > 0) { ioObject.addTimer(remoteHeartbeatTtl, HEARTBEAT_TTL_TIMER_ID); hasTtlTimer = true; } // extract the ping context that will be sent back inside the pong message int remaining = msg.size() - 7; // As per ZMTP 3.1 the PING command might contain an up to 16 bytes // context which needs to be PONGed back, so build the pong message // here and store it. Truncate it if it's too long. // Given the engine goes straight to outEvent(), sequential PINGs will // not be a problem. if (remaining > 16) { remaining = 16; } final byte[] pingContext = new byte[remaining]; msg.getBytes(7, pingContext, 0, remaining); nextMsg = new ProducePongMessage(pingContext); outEvent(); return true; } // Writes data to the socket. Returns the number of bytes actually // written (even zero is to be considered to be a success). In case // of error or orderly shutdown by the other peer -1 is returned. private int write(ByteBuffer outbuf) { int nbytes; try { nbytes = fd.write(outbuf); if (nbytes == 0) { errno.set(ZError.EAGAIN); } } catch (IOException e) { errno.set(ZError.ENOTCONN); nbytes = -1; } return nbytes; } // Reads data from the socket (up to 'size' bytes). // Returns the number of bytes actually read or -1 on error. // Zero indicates the peer has closed the connection. private int read(ByteBuffer buf) { int nbytes; try { nbytes = fd.read(buf); if (nbytes == -1) { errno.set(ZError.ENOTCONN); } else if (nbytes == 0) { if (!fd.isBlocking()) { // If not a single byte can be read from the socket in non-blocking mode // we'll get an error (this may happen during the speculative read). // Several errors are OK. When speculative read is being done we may not // be able to read a single byte from the socket. Also, SIGSTOP issued // by a debugging tool can result in EINTR error. errno.set(ZError.EAGAIN); nbytes = -1; } } } catch (IOException e) { errno.set(ZError.ENOTCONN); nbytes = -1; } return nbytes; } @Override public String getEndPoint() { return endpoint; } @Override public String toString() { return getClass().getSimpleName() + socket + "-" + zmtpVersion; } } jeromq-0.6.0/src/main/java/zmq/io/coder/000077500000000000000000000000001455771126300177735ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/coder/Decoder.java000066400000000000000000000073121455771126300222060ustar00rootroot00000000000000package zmq.io.coder; import zmq.Msg; import zmq.ZError; import zmq.msg.MsgAllocator; import zmq.util.Errno; // Helper base class for decoders that know the amount of data to read // in advance at any moment. Knowing the amount in advance is a property // of the protocol used. 0MQ framing protocol is based size-prefixed // paradigm, which qualifies it to be parsed by this class. // On the other hand, XML-based transports (like XMPP or SOAP) don't allow // for knowing the size of data to read in advance and should use different // decoding algorithms. // // This class implements the state machine that parses the incoming buffer. // Derived class should implement individual state machine actions. public abstract class Decoder extends DecoderBase { private final class MessageReady implements Step { @Override public Step.Result apply() { return messageReady(); } } private final class FlagsReady implements Step { @Override public Step.Result apply() { return flagsReady(); } } private final class EightByteSizeReady implements Step { @Override public Step.Result apply() { return eightByteSizeReady(); } } private final class OneByteSizeReady implements Step { @Override public Step.Result apply() { return oneByteSizeReady(); } } protected final long maxmsgsize; // the message decoded so far protected Msg inProgress; protected final Step oneByteSizeReady = new OneByteSizeReady(); protected final Step eightByteSizeReady = new EightByteSizeReady(); protected final Step flagsReady = new FlagsReady(); protected final Step messageReady = new MessageReady(); private final MsgAllocator allocator; public Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator) { super(errno, bufsize); this.maxmsgsize = maxmsgsize; this.allocator = allocator; } protected final Step.Result sizeReady(final long size) { // Message size must not exceed the maximum allowed size. if (maxmsgsize >= 0) { if (size > maxmsgsize) { errno(ZError.EMSGSIZE); return Step.Result.ERROR; } } // Message size must fit within range of size_t data type. if (size > Integer.MAX_VALUE) { errno(ZError.EMSGSIZE); return Step.Result.ERROR; } // inProgress is initialized at this point so in theory we should // close it before calling init_size, however, it's a 0-byte // message and thus we can treat it as uninitialized. inProgress = allocate((int) size); return Step.Result.MORE_DATA; } protected Msg allocate(final int size) { return allocator.allocate(size); } protected Step.Result oneByteSizeReady() { throw new UnsupportedOperationException("Have you forgot to implement oneByteSizeReady ?"); } protected Step.Result eightByteSizeReady() { throw new UnsupportedOperationException("Have you forgot to implement eightByteSizeReady ?"); } protected Step.Result flagsReady() { throw new UnsupportedOperationException("Have you forgot to implement flagsReady ?"); } protected Step.Result messageReady() { throw new UnsupportedOperationException("Have you forgot to implement messageReady ?"); } protected Step.Result messageIncomplete() { return Step.Result.MORE_DATA; } @Override public Msg msg() { return inProgress; } } jeromq-0.6.0/src/main/java/zmq/io/coder/DecoderBase.java000066400000000000000000000112241455771126300227760ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import zmq.Msg; import zmq.util.Errno; import zmq.util.ValueReference; // Helper base class for decoders that know the amount of data to read // in advance at any moment. Knowing the amount in advance is a property // of the protocol used. 0MQ framing protocol is based size-prefixed // paradigm, which qualifies it to be parsed by this class. // On the other hand, XML-based transports (like XMPP or SOAP) don't allow // for knowing the size of data to read in advance and should use different // decoding algorithms. // // This class implements the state machine that parses the incoming buffer. // Derived class should implement individual state machine actions. public abstract class DecoderBase implements IDecoder { // Where to store the read data. private ByteBuffer readPos; // TODO V4 remove zeroCopy boolean private boolean zeroCopy; // How much data to read before taking next step. private int toRead; // The buffer for data to decode. private final int bufsize; private final ByteBuffer buf; private Step next; private final Errno errno; public DecoderBase(Errno errno, int bufsize) { next = null; readPos = null; toRead = 0; this.bufsize = bufsize; assert (bufsize > 0); buf = ByteBuffer.allocateDirect(bufsize); this.errno = errno; } // Returns a buffer to be filled with binary data. @Override public ByteBuffer getBuffer() { // If we are expected to read large message, we'll opt for zero- // copy, i.e. we'll ask caller to fill the data directly to the // message. Note that subsequent read(s) are non-blocking, thus // each single read reads at most SO_RCVBUF bytes at once not // depending on how large is the chunk returned from here. // As a consequence, large messages being received won't block // other engines running in the same I/O thread for excessive // amounts of time. if (toRead >= bufsize) { zeroCopy = true; return readPos.duplicate(); } else { zeroCopy = false; buf.clear(); return buf; } } // Processes the data in the buffer previously allocated using // get_buffer function. size_ argument specifies number of bytes // actually filled into the buffer. Function returns number of // bytes actually processed. @Override public Step.Result decode(ByteBuffer data, int size, ValueReference processed) { processed.set(0); // In case of zero-copy simply adjust the pointers, no copying // is required. Also, run the state machine in case all the data // were processed. if (zeroCopy) { assert (size <= toRead); readPos.position(readPos.position() + size); toRead -= size; processed.set(size); while (readPos.remaining() == 0) { Step.Result result = next.apply(); if (result != Step.Result.MORE_DATA) { return result; } } return Step.Result.MORE_DATA; } while (processed.get() < size) { // Copy the data from buffer to the message. int toCopy = Math.min(toRead, size - processed.get()); int limit = data.limit(); data.limit(data.position() + toCopy); readPos.put(data); data.limit(limit); toRead -= toCopy; processed.set(processed.get() + toCopy); // Try to get more space in the message to fill in. // If none is available, return. while (readPos.remaining() == 0) { Step.Result result = next.apply(); if (result != Step.Result.MORE_DATA) { return result; } } } return Step.Result.MORE_DATA; } protected void nextStep(Msg msg, Step next) { nextStep(msg.buf(), next); } @Deprecated protected void nextStep(byte[] buf, int toRead, Step next) { readPos = ByteBuffer.wrap(buf); readPos.limit(toRead); this.toRead = toRead; this.next = next; } protected void nextStep(ByteBuffer buf, Step next) { readPos = buf; this.toRead = buf.remaining(); this.next = next; } protected void errno(int err) { this.errno.set(err); } public int errno() { return errno.get(); } @Override public void destroy() { } } jeromq-0.6.0/src/main/java/zmq/io/coder/Encoder.java000066400000000000000000000006231455771126300222160ustar00rootroot00000000000000package zmq.io.coder; import zmq.util.Errno; public abstract class Encoder extends EncoderBase { protected final Runnable sizeReady = this::sizeReady; protected final Runnable messageReady = this::messageReady; protected Encoder(Errno errno, int bufsize) { super(errno, bufsize); } protected abstract void sizeReady(); protected abstract void messageReady(); } jeromq-0.6.0/src/main/java/zmq/io/coder/EncoderBase.java000066400000000000000000000126361455771126300230200ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import zmq.Msg; import zmq.util.Errno; import zmq.util.ValueReference; public abstract class EncoderBase implements IEncoder { // Where to get the data to write from. private ByteBuffer writeBuf; // Next step. If set to null, it means that associated data stream // is dead. private Runnable next; // If true, first byte of the message is being written. private boolean newMsgFlag; // How much data to write before next step should be executed. private int toWrite; // The buffer for encoded data. private final ByteBuffer buffer; private final int bufferSize; private boolean error; protected Msg inProgress; private final Errno errno; protected EncoderBase(Errno errno, int bufferSize) { this.errno = errno; this.bufferSize = bufferSize; buffer = ByteBuffer.allocateDirect(bufferSize); error = false; } // Load a new message into encoder. @Override public final void loadMsg(Msg msg) { assert (inProgress == null); inProgress = msg; next(); } // The function returns a batch of binary data. The data // are filled to a supplied buffer. If no buffer is supplied (data // is NULL) encoder will provide buffer of its own. @Override public final int encode(ValueReference data, int size) { int bufferSize = size; ByteBuffer buf = data.get(); if (buf == null) { buf = this.buffer; bufferSize = this.bufferSize; buffer.clear(); } if (inProgress == null) { return 0; } int pos = 0; buf.limit(buf.capacity()); while (pos < bufferSize) { // If there are no more data to return, run the state machine. // If there are still no data, return what we already have // in the buffer. if (toWrite == 0) { if (newMsgFlag) { inProgress = null; break; } next(); } // If there are no data in the buffer yet and we are able to // fill whole buffer in a single go, let's use zero-copy. // There's no disadvantage to it as we cannot stuck multiple // messages into the buffer anyway. Note that subsequent // write(s) are non-blocking, thus each single write writes // at most SO_SNDBUF bytes at once not depending on how large // is the chunk returned from here. // As a consequence, large messages being sent won't block // other engines running in the same I/O thread for excessive // amounts of time. if (pos == 0 && data.get() == null && toWrite >= bufferSize) { writeBuf.limit(writeBuf.capacity()); data.set(writeBuf); pos = toWrite; writeBuf = null; toWrite = 0; return pos; } // Copy data to the buffer. If the buffer is full, return. int toCopy = Math.min(toWrite, bufferSize - pos); int limit = writeBuf.limit(); writeBuf.limit(Math.min(writeBuf.capacity(), writeBuf.position() + toCopy)); int current = buf.position(); buf.put(writeBuf); toCopy = buf.position() - current; writeBuf.limit(limit); pos += toCopy; toWrite -= toCopy; } data.set(buf); return pos; } @Override public void encoded() { buffer.flip(); } protected void encodingError() { error = true; } public final boolean isError() { return error; } protected void next() { if (next != null) { next.run(); } } protected void nextStep(Msg msg, Runnable state, boolean beginning) { if (msg == null) { nextStep((byte[]) null, 0, state, beginning); } else { nextStep(msg.buf(), state, beginning); } } // This function should be called from derived class to write the data // to the buffer and schedule next state machine action. private void nextStep(byte[] buf, int toWrite, Runnable next, boolean newMsgFlag) { if (buf != null) { writeBuf = ByteBuffer.wrap(buf); writeBuf.limit(toWrite); } else { writeBuf = null; } this.toWrite = toWrite; this.next = next; this.newMsgFlag = newMsgFlag; } protected void initStep(Runnable next, boolean newMsgFlag) { nextStep((byte[]) null, 0, next, newMsgFlag); } private void nextStep(ByteBuffer buf, Runnable next, boolean newMsgFlag) { nextStep(buf, buf.limit(), next, newMsgFlag); } protected void nextStep(ByteBuffer buf, int toWrite, Runnable next, boolean newMsgFlag) { buf.limit(toWrite); buf.position(toWrite); buf.flip(); writeBuf = buf; this.toWrite = toWrite; this.next = next; this.newMsgFlag = newMsgFlag; } public int errno() { return errno.get(); } public void errno(int err) { this.errno.set(err); } @Override public void destroy() { } } jeromq-0.6.0/src/main/java/zmq/io/coder/IDecoder.java000066400000000000000000000006751455771126300223240ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import zmq.Msg; import zmq.util.ValueReference; public interface IDecoder { interface Step { enum Result { MORE_DATA, DECODED, ERROR; } Result apply(); } ByteBuffer getBuffer(); Step.Result decode(ByteBuffer buffer, int size, ValueReference processed); Msg msg(); void destroy(); } jeromq-0.6.0/src/main/java/zmq/io/coder/IEncoder.java000066400000000000000000000011551455771126300223300ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import zmq.Msg; import zmq.util.ValueReference; public interface IEncoder { // Load a new message into encoder. void loadMsg(Msg msg); // The function returns a batch of binary data. The data // are filled to a supplied buffer. If no buffer is supplied (data_ // points to NULL) decoder object will provide buffer of its own. int encode(ValueReference data, int size); void destroy(); // called when stream engine finished encoding all messages and is ready to // send data to network layer void encoded(); } jeromq-0.6.0/src/main/java/zmq/io/coder/raw/000077500000000000000000000000001455771126300205645ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/coder/raw/RawDecoder.java000066400000000000000000000016221455771126300234470ustar00rootroot00000000000000package zmq.io.coder.raw; import java.nio.ByteBuffer; import zmq.Msg; import zmq.io.coder.IDecoder; import zmq.util.ValueReference; public class RawDecoder implements IDecoder { // The buffer for data to decode. private final ByteBuffer buffer; protected Msg inProgress; public RawDecoder(int bufsize) { buffer = ByteBuffer.allocateDirect(bufsize); inProgress = new Msg(); } @Override public ByteBuffer getBuffer() { buffer.clear(); return buffer; } @Override public Step.Result decode(ByteBuffer buffer, int size, ValueReference processed) { processed.set(size); inProgress = new Msg(size); inProgress.put(buffer); return Step.Result.DECODED; } @Override public Msg msg() { return inProgress; } @Override public void destroy() { } } jeromq-0.6.0/src/main/java/zmq/io/coder/raw/RawEncoder.java000066400000000000000000000012031455771126300234540ustar00rootroot00000000000000package zmq.io.coder.raw; import zmq.io.coder.Encoder; import zmq.util.Errno; // Encoder for 0MQ framing protocol. Converts messages into data batches. public class RawEncoder extends Encoder { public RawEncoder(Errno errno, int bufsize) { super(errno, bufsize); // Write 0 bytes to the batch and go to messageReady state. initStep(messageReady, true); } @Override protected void sizeReady() { throw new UnsupportedOperationException(); } @Override protected void messageReady() { nextStep(inProgress.buf(), inProgress.size(), messageReady, true); } } jeromq-0.6.0/src/main/java/zmq/io/coder/v1/000077500000000000000000000000001455771126300203215ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Decoder.java000066400000000000000000000055321455771126300227450ustar00rootroot00000000000000package zmq.io.coder.v1; import java.nio.ByteBuffer; import zmq.Msg; import zmq.ZError; import zmq.io.coder.Decoder; import zmq.msg.MsgAllocator; import zmq.util.Errno; import zmq.util.Wire; public class V1Decoder extends Decoder { private final ByteBuffer tmpbuf; public V1Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator) { super(errno, bufsize, maxmsgsize, allocator); tmpbuf = ByteBuffer.allocate(8); tmpbuf.limit(1); // At the beginning, read one byte and go to ONE_BYTE_SIZE_READY state. nextStep(tmpbuf, oneByteSizeReady); } @Override protected Step.Result oneByteSizeReady() { // First byte of size is read. If it is 0xff read 8-byte size. // Otherwise allocate the buffer for message data and read the // message data into it. int size = tmpbuf.get(0) & 0xff; if (size == 0xff) { tmpbuf.position(0); tmpbuf.limit(8); nextStep(tmpbuf, eightByteSizeReady); } else { // There has to be at least one byte (the flags) in the message). if (size == 0) { errno(ZError.EPROTO); return Step.Result.ERROR; } tmpbuf.position(0); tmpbuf.limit(1); Step.Result rc = sizeReady(size - 1); if (rc != Step.Result.ERROR) { nextStep(tmpbuf, flagsReady); } return rc; } return Step.Result.MORE_DATA; } @Override protected Step.Result eightByteSizeReady() { // 8-byte payload length is read. Allocate the buffer // for message body and read the message data into it. tmpbuf.position(0); tmpbuf.limit(8); final long payloadLength = Wire.getUInt64(tmpbuf, 0); if (payloadLength <= 0) { errno(ZError.EPROTO); return Step.Result.ERROR; } tmpbuf.limit(1); Step.Result rc = sizeReady(payloadLength - 1); if (rc != Step.Result.ERROR) { nextStep(tmpbuf, flagsReady); } return rc; } @Override protected Step.Result flagsReady() { // Store the flags from the wire into the message structure. int first = tmpbuf.get(0) & 0xff; if ((first & V1Protocol.MORE_FLAG) > 0) { inProgress.setFlags(Msg.MORE); } nextStep(inProgress, messageReady); return Step.Result.MORE_DATA; } @Override protected Step.Result messageReady() { // Message is completely read. Push it further and start reading // new message. (inProgress is a 0-byte message after this point.) tmpbuf.position(0); tmpbuf.limit(1); nextStep(tmpbuf, oneByteSizeReady); return Step.Result.DECODED; } } jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Encoder.java000066400000000000000000000030411455771126300227500ustar00rootroot00000000000000package zmq.io.coder.v1; import java.nio.ByteBuffer; import zmq.Msg; import zmq.io.coder.Encoder; import zmq.util.Errno; import zmq.util.Wire; // Encoder for 0MQ framing protocol. Converts messages into data stream. public class V1Encoder extends Encoder { private final ByteBuffer tmpbufWrap; public V1Encoder(Errno errno, int bufsize) { super(errno, bufsize); tmpbufWrap = ByteBuffer.allocate(10); // Write 0 bytes to the batch and go to messageReady state. initStep(messageReady, true); } @Override protected void sizeReady() { // Write message body into the buffer. nextStep(inProgress.buf(), inProgress.size(), messageReady, true); } @Override protected void messageReady() { // Get the message size. int size = inProgress.size(); // Account for the 'flags' byte. size++; // For messages less than 255 bytes long, write one byte of message size. // For longer messages write 0xff escape character followed by 8-byte // message size. In both cases 'flags' field follows. tmpbufWrap.position(0); if (size < 255) { tmpbufWrap.limit(2); tmpbufWrap.put((byte) size); } else { tmpbufWrap.limit(10); tmpbufWrap.put((byte) 0xff); Wire.putUInt64(tmpbufWrap, size); } tmpbufWrap.put((byte) (inProgress.flags() & Msg.MORE)); nextStep(tmpbufWrap, tmpbufWrap.limit(), sizeReady, false); } } jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Protocol.java000066400000000000000000000002721455771126300231750ustar00rootroot00000000000000package zmq.io.coder.v1; public interface V1Protocol { int VERSION = 1; int MORE_FLAG = 1; // make checkstyle not block the release @Override String toString(); } jeromq-0.6.0/src/main/java/zmq/io/coder/v2/000077500000000000000000000000001455771126300203225ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Decoder.java000066400000000000000000000051371455771126300227500ustar00rootroot00000000000000package zmq.io.coder.v2; import java.nio.ByteBuffer; import zmq.Msg; import zmq.io.coder.Decoder; import zmq.msg.MsgAllocator; import zmq.util.Errno; import zmq.util.Wire; public class V2Decoder extends Decoder { private final ByteBuffer tmpbuf; private int msgFlags; public V2Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator) { super(errno, bufsize, maxmsgsize, allocator); tmpbuf = ByteBuffer.allocate(8); tmpbuf.limit(1); // At the beginning, read one byte and go to ONE_BYTE_SIZE_READY state. nextStep(tmpbuf, flagsReady); } @Override protected Msg allocate(int size) { Msg msg = super.allocate(size); msg.setFlags(msgFlags); return msg; } @Override protected Step.Result oneByteSizeReady() { int size = tmpbuf.get(0) & 0xff; Step.Result rc = sizeReady(size); if (rc != Step.Result.ERROR) { nextStep(inProgress, messageReady); } return rc; } @Override protected Step.Result eightByteSizeReady() { // The payload size is encoded as 64-bit unsigned integer. // The most significant byte comes first. tmpbuf.position(0); tmpbuf.limit(8); final long size = Wire.getUInt64(tmpbuf, 0); Step.Result rc = sizeReady(size); if (rc != Step.Result.ERROR) { nextStep(inProgress, messageReady); } return rc; } @Override protected Step.Result flagsReady() { // Store the flags from the wire into the message structure. this.msgFlags = 0; int first = tmpbuf.get(0) & 0xff; if ((first & V2Protocol.MORE_FLAG) > 0) { this.msgFlags |= Msg.MORE; } if ((first & V2Protocol.COMMAND_FLAG) > 0) { this.msgFlags |= Msg.COMMAND; } // The payload length is either one or eight bytes, // depending on whether the 'large' bit is set. tmpbuf.position(0); if ((first & V2Protocol.LARGE_FLAG) > 0) { tmpbuf.limit(8); nextStep(tmpbuf, eightByteSizeReady); } else { tmpbuf.limit(1); nextStep(tmpbuf, oneByteSizeReady); } return Step.Result.MORE_DATA; } @Override protected Step.Result messageReady() { // Message is completely read. Signal this to the caller // and prepare to decode next message. tmpbuf.position(0); tmpbuf.limit(1); nextStep(tmpbuf, flagsReady); return Step.Result.DECODED; } } jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Encoder.java000066400000000000000000000033321455771126300227550ustar00rootroot00000000000000package zmq.io.coder.v2; import java.nio.ByteBuffer; import zmq.io.coder.Encoder; import zmq.util.Errno; import zmq.util.Wire; // Encoder for 0MQ framing protocol. Converts messages into data stream. public class V2Encoder extends Encoder { private final ByteBuffer tmpbufWrap; public V2Encoder(Errno errno, int bufsize) { super(errno, bufsize); tmpbufWrap = ByteBuffer.allocate(9); // Write 0 bytes to the batch and go to messageReady state. initStep(messageReady, true); } @Override protected void messageReady() { // Encode flags. byte protocolFlags = 0; if (inProgress.hasMore()) { protocolFlags |= V2Protocol.MORE_FLAG; } if (inProgress.size() > 255) { protocolFlags |= V2Protocol.LARGE_FLAG; } if (inProgress.isCommand()) { protocolFlags |= V2Protocol.COMMAND_FLAG; } // Encode the message length. For messages less then 256 bytes, // the length is encoded as 8-bit unsigned integer. For larger // messages, 64-bit unsigned integer in network byte order is used. final int size = inProgress.size(); tmpbufWrap.position(0); tmpbufWrap.put(protocolFlags); if (size > 255) { tmpbufWrap.limit(9); Wire.putUInt64(tmpbufWrap, size); } else { tmpbufWrap.limit(2); tmpbufWrap.put((byte) size); } nextStep(tmpbufWrap, tmpbufWrap.limit(), sizeReady, false); } @Override protected void sizeReady() { // Write message body into the buffer. nextStep(inProgress.buf(), inProgress.size(), messageReady, true); } } jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Protocol.java000066400000000000000000000004161455771126300231770ustar00rootroot00000000000000package zmq.io.coder.v2; import zmq.io.coder.v1.V1Protocol; public interface V2Protocol extends V1Protocol { int VERSION = 2; int LARGE_FLAG = 2; int COMMAND_FLAG = 4; // make checkstyle not block the release @Override String toString(); } jeromq-0.6.0/src/main/java/zmq/io/mechanism/000077500000000000000000000000001455771126300206435ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/mechanism/Mechanism.java000066400000000000000000000255271455771126300234250ustar00rootroot00000000000000package zmq.io.mechanism; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.Metadata; import zmq.io.Msgs; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.socket.Sockets; import zmq.util.Blob; import zmq.util.Wire; // Abstract class representing security mechanism. // Different mechanism extends this class. public abstract class Mechanism { public enum Status { HANDSHAKING, READY, ERROR } protected final Options options; private Blob identity; private Blob userId; // Properties received from ZAP server. public final Metadata zapProperties = new Metadata(); // Properties received from ZMTP peer. public final Metadata zmtpProperties = new Metadata(); protected final SessionBase session; private final Address peerAddress; protected String statusCode; protected Mechanism(SessionBase session, Address peerAddress, Options options) { this.session = session; this.options = options; this.peerAddress = peerAddress; } public abstract Status status(); private void setPeerIdentity(byte[] data) { identity = Blob.createBlob(data); } public final Msg peerIdentity() { byte[] data = new byte[0]; int size = 0; if (identity != null) { data = identity.data(); size = identity.size(); } Msg msg = new Msg(size); msg.put(data, 0, size); msg.setFlags(Msg.IDENTITY); return msg; } private void setUserId(byte[] data) { userId = Blob.createBlob(data); zapProperties.put(Metadata.USER_ID, new String(data, ZMQ.CHARSET)); } public final Blob getUserId() { return userId; } protected final void addProperty(ByteBuffer buf, String name, String value) { addProperty(buf, name, value.getBytes(ZMQ.CHARSET)); } protected final void addProperty(Msg msg, String name, String value) { addProperty(msg, name, value.getBytes(ZMQ.CHARSET)); } protected final void addProperty(ByteBuffer buf, String name, byte[] value) { byte[] nameB = name.getBytes(ZMQ.CHARSET); int nameLength = nameB.length; assert (nameLength <= 255); int valueLength = value == null ? 0 : value.length; buf.put((byte) nameLength); buf.put(nameB); Wire.putUInt32(buf, valueLength); if (value != null) { buf.put(value); } } protected final void addProperty(Msg msg, String name, byte[] value) { byte[] nameB = name.getBytes(ZMQ.CHARSET); int nameLength = nameB.length; assert (nameLength <= 255); int valueLength = value == null ? 0 : value.length; msg.put((byte) nameLength); msg.put(nameB); Wire.putUInt32(msg, valueLength); if (value != null) { msg.put(value); } } protected final int parseMetadata(Msg msg, int offset, boolean zapFlag) { return parseMetadata(msg.buf(), offset, zapFlag); } protected final int parseMetadata(ByteBuffer msg, int offset, boolean zapFlag) { Metadata meta = zapFlag ? zapProperties : zmtpProperties; return meta.read(msg, offset, (name, value, valueAsString) -> { int type = options.asType != -1 ? options.asType : options.type; if (IDENTITY.equals(name) && options.recvIdentity) { setPeerIdentity(value); } else if (SOCKET_TYPE.equals(name)) { if (!Sockets.compatible(type, valueAsString)) { return ZError.EINVAL; } } else { int rc = property(name, value); if (rc == -1) { return -1; } } // continue return 0; }); } protected int property(String name, byte[] value) { // Default implementation does not check // property values and returns 0 to signal success. return 0; } protected final String socketType() { if (options.asType != -1) { return Sockets.name(options.asType); } else { return Sockets.name(options.type); } } protected boolean compare(Msg msg, String data, boolean includeLength) { return Msgs.startsWith(msg, data, includeLength); } protected boolean compare(ByteBuffer a1, byte[] b, int offset, int length) { if (length > b.length) { return false; } boolean comparison = true; for (int idx = 0; idx < length; ++idx) { comparison = a1.get(idx + offset) == b[idx]; if (!comparison) { break; } } return comparison; } public Msg decode(Msg msg) { return msg; } public Msg encode(Msg msg) { return msg; } public abstract int zapMsgAvailable(); public abstract int processHandshakeCommand(Msg msg); public abstract int nextHandshakeCommand(Msg msg); protected int parseErrorMessage(Msg msg) { if (msg.size() < 7 && msg.size() != 6) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); return ZError.EPROTO; } if (msg.size() >= 7) { byte errorReasonLength = msg.get(6); if (errorReasonLength > msg.size() - 7) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR); return ZError.EPROTO; } // triple digit status code if (msg.size() == 10) { byte[] statusBuffer = Arrays.copyOfRange(msg.data(), 7, 10); String statusCode = new String(statusBuffer, ZMQ.CHARSET); if (handleErrorReason(statusCode) < 0) { return ZError.EPROTO; } } } return 0; } protected int handleErrorReason(String reason) { int rc = -1; if (reason.length() == 3 && reason.charAt(1) == '0' && reason.charAt(2) == '0' && reason.charAt(0) >= '3' && reason.charAt(0) <= '5') { try { int statusCode = Integer.parseInt(reason); session.getSocket().eventHandshakeFailedAuth(session.getEndpoint(), statusCode); rc = 0; } catch (NumberFormatException e) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY); } } else { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY); } return rc; } protected final void sendZapRequest(Mechanisms mechanism, boolean more) { assert (session != null); assert (peerAddress != null); assert (mechanism != null); Msg msg = new Msg(); // Address delimiter frame msg.setFlags(Msg.MORE); boolean rc = session.writeZapMsg(msg); assert (rc); // Version frame msg = new Msg(3); msg.setFlags(Msg.MORE); msg.put("1.0".getBytes(ZMQ.CHARSET)); rc = session.writeZapMsg(msg); assert (rc); // Request id frame msg = new Msg(1); msg.setFlags(Msg.MORE); msg.put("1".getBytes(ZMQ.CHARSET)); rc = session.writeZapMsg(msg); assert (rc); // Domain frame msg = new Msg(options.zapDomain.length()); msg.setFlags(Msg.MORE); msg.put(options.zapDomain.getBytes(ZMQ.CHARSET)); rc = session.writeZapMsg(msg); assert (rc); // Address frame byte[] host = peerAddress.host().getBytes(ZMQ.CHARSET); msg = new Msg(host.length); msg.setFlags(Msg.MORE); msg.put(host); rc = session.writeZapMsg(msg); assert (rc); // Identity frame msg = new Msg(options.identitySize); msg.setFlags(Msg.MORE); msg.put(options.identity, 0, options.identitySize); rc = session.writeZapMsg(msg); assert (rc); // Mechanism frame msg = new Msg(mechanism.name().length()); msg.put(mechanism.name().getBytes(ZMQ.CHARSET)); if (more) { msg.setFlags(Msg.MORE); } rc = session.writeZapMsg(msg); assert (rc); } protected final int receiveAndProcessZapReply() { assert (session != null); List msgs = new ArrayList<>(7); // ZAP reply consists of 7 frames // Initialize all reply frames for (int idx = 0; idx < 7; ++idx) { Msg msg = session.readZapMsg(); if (msg == null) { return session.errno.get(); } if ((msg.flags() & Msg.MORE) == (idx < 6 ? 0 : Msg.MORE)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY); return ZError.EPROTO; } msgs.add(msg); } // Address delimiter frame if (msgs.get(0).size() > 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED); return ZError.EPROTO; } // Version frame if (msgs.get(1).size() != 3 || !compare(msgs.get(1), "1.0", false)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION); return ZError.EPROTO; } // Request id frame if (msgs.get(2).size() != 1 || !compare(msgs.get(2), "1", false)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID); return ZError.EPROTO; } // Status code frame if (msgs.get(3).size() != 3) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE); return ZError.EPROTO; } // Save status code statusCode = new String(msgs.get(3).data(), ZMQ.CHARSET); // Save user id setUserId(msgs.get(5).data()); // Process metadata frame return parseMetadata(msgs.get(6), 0, true); } public void destroy() { } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/Mechanisms.java000066400000000000000000000102001455771126300235660ustar00rootroot00000000000000package zmq.io.mechanism; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import zmq.Options; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.mechanism.curve.CurveClientMechanism; import zmq.io.mechanism.curve.CurveServerMechanism; import zmq.io.mechanism.gssapi.GssapiClientMechanism; import zmq.io.mechanism.gssapi.GssapiServerMechanism; import zmq.io.mechanism.plain.PlainClientMechanism; import zmq.io.mechanism.plain.PlainServerMechanism; import zmq.io.net.Address; public enum Mechanisms { NULL { @Override public void check(Options options) { // Nothing to check } @Override public Mechanism create(SessionBase session, Address peerAddress, Options options) { return new NullMechanism(session, peerAddress, options); } }, PLAIN { @Override public void check(Options options) { if (! options.asServer) { Set errors = new HashSet<>(2); if (options.plainUsername == null || options.plainUsername.length() >= 256) { errors.add("user name invalid"); } if (options.plainPassword == null || options.plainPassword.length() >= 256) { errors.add("password is invalid"); } if (!errors.isEmpty()) { throw new IllegalStateException("Plain mechanism definition incomplete: " + errors); } } } @Override public Mechanism create(SessionBase session, Address peerAddress, Options options) { if (options.asServer) { return new PlainServerMechanism(session, peerAddress, options); } else { return new PlainClientMechanism(session, options); } } }, CURVE { @Override public void check(Options options) { Set errors = new HashSet<>(3); if (options.curvePublicKey == null || options.curvePublicKey.length != Options.CURVE_KEYSIZE) { errors.add("public key is invalid"); } if (options.curveSecretKey == null || options.curveSecretKey.length != Options.CURVE_KEYSIZE) { errors.add("secret key is invalid"); } if (!options.asServer && (options.curveServerKey == null || options.curveServerKey.length != Options.CURVE_KEYSIZE)) { errors.add("not a server and no server public key given"); } if (!errors.isEmpty()) { throw new IllegalStateException("Curve mechanism definition incomplete: " + errors); } } @Override public Mechanism create(SessionBase session, Address peerAddress, Options options) { if (options.asServer) { return new CurveServerMechanism(session, peerAddress, options); } else { return new CurveClientMechanism(session, options); } } }, GSSAPI { @Override public void check(Options options) { throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented"); } @Override public Mechanism create(SessionBase session, Address peerAddress, Options options) { if (options.asServer) { return new GssapiServerMechanism(session, peerAddress, options); } else { return new GssapiClientMechanism(session, options); } } }; public abstract Mechanism create(SessionBase session, Address peerAddress, Options options); public abstract void check(Options options); public boolean isMechanism(ByteBuffer greetingRecv) { byte[] dst = new byte[20]; greetingRecv.get(dst, 0, dst.length); byte[] name = name().getBytes(ZMQ.CHARSET); byte[] comp = Arrays.copyOf(name, 20); return Arrays.equals(dst, comp); } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/NullMechanism.java000066400000000000000000000100161455771126300242430ustar00rootroot00000000000000package zmq.io.mechanism; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.net.Address; class NullMechanism extends Mechanism { private static final String OK = "200"; private static final String READY = "READY"; private static final String ERROR = "ERROR"; private boolean readyCommandSent; private boolean errorCommandSent; private boolean readyCommandReceived; private boolean errorCommandReceived; private boolean zapConnected; private boolean zapRequestSent; private boolean zapReplyReceived; NullMechanism(SessionBase session, Address peerAddress, Options options) { super(session, peerAddress, options); // NULL mechanism only uses ZAP if there's a domain defined // This prevents ZAP requests on naive sockets if (options.zapDomain != null && !options.zapDomain.isEmpty() && session.zapConnect() == 0) { zapConnected = true; } } @Override public int nextHandshakeCommand(Msg msg) { if (readyCommandSent || errorCommandSent) { return ZError.EAGAIN; } if (zapConnected && !zapReplyReceived) { if (zapRequestSent) { return ZError.EAGAIN; } sendZapRequest(Mechanisms.NULL, false); zapRequestSent = true; int rc = receiveAndProcessZapReply(); if (rc != 0) { return rc; } zapReplyReceived = true; } if (zapReplyReceived && !OK.equals(statusCode)) { msg.putShortString(ERROR); msg.putShortString(statusCode); errorCommandSent = true; return 0; } // Add mechanism string msg.putShortString(READY); // Add socket type property String socketType = socketType(); addProperty(msg, SOCKET_TYPE, socketType); // Add identity property if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) { addProperty(msg, IDENTITY, options.identity); } readyCommandSent = true; return 0; } @Override public int processHandshakeCommand(Msg msg) { if (readyCommandReceived || errorCommandReceived) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } int dataSize = msg.size(); int rc; if (dataSize >= 6 && compare(msg, READY, true)) { rc = processReadyCommand(msg); } else if (dataSize >= 6 && compare(msg, ERROR, true)) { rc = processErrorCommand(msg); } else { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } return rc; } private int processReadyCommand(Msg msg) { readyCommandReceived = true; return parseMetadata(msg, 6, false); } private int processErrorCommand(Msg msg) { errorCommandReceived = true; return parseErrorMessage(msg); } @Override public int zapMsgAvailable() { if (zapReplyReceived) { return ZError.EFSM; } int rc = receiveAndProcessZapReply(); if (rc == 0) { zapReplyReceived = true; } return rc; } @Override public Status status() { boolean commandSent = readyCommandSent || errorCommandSent; boolean commandReceived = readyCommandReceived || errorCommandReceived; if (readyCommandSent && readyCommandReceived) { return Status.READY; } if (commandSent && commandReceived) { return Status.ERROR; } return Status.HANDSHAKING; } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/000077500000000000000000000000001455771126300217675ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/Curve.java000066400000000000000000000140151455771126300237170ustar00rootroot00000000000000package zmq.io.mechanism.curve; import java.nio.ByteBuffer; import com.neilalexander.jnacl.crypto.curve25519xsalsa20poly1305; import com.neilalexander.jnacl.crypto.xsalsa20poly1305; import zmq.util.Utils; import zmq.util.Z85; // wrapper around the wrapper of libsodium (ahem), for shorter names. public class Curve { enum Size { NONCE { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_NONCEBYTES; } }, ZERO { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_ZEROBYTES; } }, BOXZERO { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_BOXZEROBYTES; } }, PUBLICKEY { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_PUBLICKEYBYTES; } }, SECRETKEY { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_SECRETKEYBYTES; } }, KEY { @Override public int bytes() { return 32; } }, BEFORENM { @Override public int bytes() { return curve25519xsalsa20poly1305.crypto_secretbox_BEFORENMBYTES; } }; public abstract int bytes(); } public Curve() { } public static String z85EncodePublic(byte[] publicKey) { return Z85.encode(publicKey, Size.PUBLICKEY.bytes()); } /** * Generates a pair of Z85-encoded keys for use with this class. * * @return an array of 2 strings, holding Z85-encoded keys. * The first element of the array is the public key, * the second element is the private (or secret) key. */ public String[] keypairZ85() { String[] pair = new String[2]; byte[] publicKey = new byte[Size.PUBLICKEY.bytes()]; byte[] secretKey = new byte[Size.SECRETKEY.bytes()]; int rc = curve25519xsalsa20poly1305.crypto_box_keypair(publicKey, secretKey); assert (rc == 0); pair[0] = Z85.encode(publicKey, Size.PUBLICKEY.bytes()); pair[1] = Z85.encode(secretKey, Size.SECRETKEY.bytes()); return pair; } /** * Generates a pair of keys for use with this class. * * @return an array of 2 byte arrays, holding keys. * The first element of the array is the public key, * the second element is the private (or secret) key. */ public byte[][] keypair() { byte[][] pair = new byte[2][]; byte[] publicKey = new byte[Size.PUBLICKEY.bytes()]; byte[] secretKey = new byte[Size.SECRETKEY.bytes()]; int rc = curve25519xsalsa20poly1305.crypto_box_keypair(publicKey, secretKey); assert (rc == 0); pair[0] = publicKey; pair[1] = secretKey; return pair; } int beforenm(byte[] outSharedKey, byte[] publicKey, byte[] secretKey) { return curve25519xsalsa20poly1305.crypto_box_beforenm(outSharedKey, publicKey, secretKey); } int afternm(ByteBuffer ciphered, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] precom) { return afternm(ciphered.array(), plaintext.array(), length, nonce.array(), precom); } int afternm(byte[] ciphered, byte[] plaintext, int length, byte[] nonce, byte[] precomp) { return curve25519xsalsa20poly1305.crypto_box_afternm(ciphered, plaintext, length, nonce, precomp); } int openAfternm(ByteBuffer plaintext, ByteBuffer messagebox, int length, ByteBuffer nonce, byte[] precom) { return openAfternm(plaintext.array(), messagebox.array(), length, nonce.array(), precom); } int openAfternm(byte[] plaintext, byte[] cipher, int length, byte[] nonce, byte[] precom) { return curve25519xsalsa20poly1305.crypto_box_open_afternm(plaintext, cipher, length, nonce, precom); } int open(ByteBuffer plaintext, ByteBuffer messagebox, int length, ByteBuffer nonce, byte[] precom, byte[] secretKey) { return open(plaintext.array(), messagebox.array(), length, nonce.array(), precom, secretKey); } int open(byte[] plaintext, byte[] messagebox, int length, byte[] nonce, byte[] publicKey, byte[] secretKey) { return curve25519xsalsa20poly1305.crypto_box_open(plaintext, messagebox, length, nonce, publicKey, secretKey); } int secretbox(ByteBuffer ciphertext, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] key) { return secretbox(ciphertext.array(), plaintext.array(), length, nonce.array(), key); } int secretbox(byte[] ciphertext, byte[] plaintext, int length, byte[] nonce, byte[] key) { return xsalsa20poly1305.crypto_secretbox(ciphertext, plaintext, length, nonce, key); } int secretboxOpen(ByteBuffer plaintext, ByteBuffer box, int length, ByteBuffer nonce, byte[] key) { return secretboxOpen(plaintext.array(), box.array(), length, nonce.array(), key); } int secretboxOpen(byte[] plaintext, byte[] box, int length, byte[] nonce, byte[] key) { return xsalsa20poly1305.crypto_secretbox_open(plaintext, box, length, nonce, key); } byte[] random(int length) { return Utils.randomBytes(length); } public int box(ByteBuffer ciphertext, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] publicKey, byte[] secretKey) { return box(ciphertext.array(), plaintext.array(), length, nonce.array(), publicKey, secretKey); } public int box(byte[] ciphertext, byte[] plaintext, int length, byte[] nonce, byte[] publicKey, byte[] secretKey) { return curve25519xsalsa20poly1305.crypto_box(ciphertext, plaintext, length, nonce, publicKey, secretKey); } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/CurveClientMechanism.java000066400000000000000000000351371455771126300267130ustar00rootroot00000000000000package zmq.io.mechanism.curve; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import java.nio.ByteBuffer; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; import zmq.util.Errno; import zmq.util.Wire; public class CurveClientMechanism extends Mechanism { private enum State { SEND_HELLO, EXPECT_WELCOME, SEND_INITIATE, EXPECT_READY, ERROR_RECEIVED, CONNECTED } private State state; // Our public key (C) private final byte[] publicKey; // Our secret key (c) private final byte[] secretKey; // Our short-term public key (C') private final byte[] cnPublic; // Our short-term secret key (c') private final byte[] cnSecret; // Server's public key (S) private final byte[] serverKey; // Server's short-term public key (S') private final byte[] cnServer = new byte[Curve.Size.PUBLICKEY.bytes()]; // Cookie received from server private final byte[] cnCookie = new byte[16 + 80]; // Intermediary buffer used to speed up boxing and unboxing. private final byte[] cnPrecom = new byte[Curve.Size.BEFORENM.bytes()]; private long cnNonce; private long cnPeerNonce; private final Curve cryptoBox; private final Errno errno; public CurveClientMechanism(SessionBase session, Options options) { super(session, null, options); this.state = State.SEND_HELLO; cnNonce = 1; cnPeerNonce = 1; publicKey = options.curvePublicKey; assert (publicKey != null && publicKey.length == Curve.Size.PUBLICKEY.bytes()); secretKey = options.curveSecretKey; assert (secretKey != null && secretKey.length == Curve.Size.SECRETKEY.bytes()); serverKey = options.curveServerKey; assert (serverKey != null && serverKey.length == Curve.Size.PUBLICKEY.bytes()); cryptoBox = new Curve(); // Generate short-term key pair byte[][] keys = cryptoBox.keypair(); assert (keys != null && keys.length == 2); cnPublic = keys[0]; assert (cnPublic != null && cnPublic.length == Curve.Size.PUBLICKEY.bytes()); cnSecret = keys[1]; assert (cnSecret != null && cnSecret.length == Curve.Size.SECRETKEY.bytes()); errno = options.errno; } @Override public int nextHandshakeCommand(Msg msg) { int rc; switch (state) { case SEND_HELLO: rc = produceHello(msg); if (rc == 0) { state = State.EXPECT_WELCOME; } break; case SEND_INITIATE: rc = produceInitiate(msg); if (rc == 0) { state = State.EXPECT_READY; } break; default: rc = ZError.EAGAIN; break; } return rc; } @Override public int processHandshakeCommand(Msg msg) { int rc; int dataSize = msg.size(); if (dataSize >= 8 && compare(msg, "WELCOME", true)) { rc = processWelcome(msg); } else if (dataSize >= 6 && compare(msg, "READY", true)) { rc = processReady(msg); } else if (dataSize >= 6 && compare(msg, "ERROR", true)) { rc = processError(msg); } else { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); rc = ZError.EPROTO; } return rc; } @Override public Msg encode(Msg msg) { assert (state == State.CONNECTED); byte flags = 0; if (msg.hasMore()) { flags |= 0x01; } if (msg.isCommand()) { flags |= 0x02; } ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); messageNonce.put("CurveZMQMESSAGEC".getBytes(ZMQ.CHARSET)); Wire.putUInt64(messageNonce, cnNonce); int mlen = Curve.Size.ZERO.bytes() + 1 + msg.size(); ByteBuffer messagePlaintext = ByteBuffer.allocate(mlen); messagePlaintext.put(Curve.Size.ZERO.bytes(), flags); messagePlaintext.position(Curve.Size.ZERO.bytes() + 1); msg.transfer(messagePlaintext, 0, msg.size()); ByteBuffer messageBox = ByteBuffer.allocate(mlen); int rc = cryptoBox.afternm(messageBox, messagePlaintext, mlen, messageNonce, cnPrecom); assert (rc == 0); Msg encoded = new Msg(16 + mlen - Curve.Size.BOXZERO.bytes()); encoded.putShortString("MESSAGE"); encoded.put(messageNonce, 16, 8); encoded.put(messageBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes()); cnNonce++; return encoded; } @Override public Msg decode(Msg msg) { assert (state == State.CONNECTED); if (!compare(msg, "MESSAGE", true)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); errno.set(ZError.EPROTO); return null; } if (msg.size() < 33) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE); errno.set(ZError.EPROTO); return null; } ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); messageNonce.put("CurveZMQMESSAGES".getBytes(ZMQ.CHARSET)); msg.transfer(messageNonce, 8, 8); long nonce = msg.getLong(8); if (nonce <= cnPeerNonce) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); errno.set(ZError.EPROTO); return null; } cnPeerNonce = nonce; int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 16; ByteBuffer messagePlaintext = ByteBuffer.allocate(clen); ByteBuffer messageBox = ByteBuffer.allocate(clen); messageBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(messageBox, 16, msg.size() - 16); int rc = cryptoBox.openAfternm(messagePlaintext, messageBox, clen, messageNonce, cnPrecom); if (rc == 0) { Msg decoded = new Msg(clen - 1 - Curve.Size.ZERO.bytes()); byte flags = messagePlaintext.get(Curve.Size.ZERO.bytes()); if ((flags & 0x01) != 0) { decoded.setFlags(Msg.MORE); } if ((flags & 0x02) != 0) { decoded.setFlags(Msg.COMMAND); } messagePlaintext.position(Curve.Size.ZERO.bytes() + 1); decoded.put(messagePlaintext); return decoded; } else { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); errno.set(ZError.EPROTO); return null; } } @Override public Status status() { if (state == State.CONNECTED) { return Status.READY; } else if (state == State.ERROR_RECEIVED) { return Status.ERROR; } else { return Status.HANDSHAKING; } } @Override public int zapMsgAvailable() { return 0; } private int produceHello(Msg msg) { ByteBuffer helloNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer helloPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer helloBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); // Prepare the full nonce helloNonce.put("CurveZMQHELLO---".getBytes(ZMQ.CHARSET)); Wire.putUInt64(helloNonce, cnNonce); // Create Box [64 * %x0](C'->S) int rc = cryptoBox.box(helloBox, helloPlaintext, helloPlaintext.capacity(), helloNonce, serverKey, cnSecret); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return -1; } msg.putShortString("HELLO"); // CurveZMQ major and minor version numbers msg.put(1); msg.put(0); // Anti-amplification padding msg.put(new byte[72]); // Client public connection key msg.put(cnPublic); // Short nonce, prefixed by "CurveZMQHELLO---" msg.put(helloNonce, 16, 8); // Signature, Box [64 * %x0](C'->S) msg.put(helloBox, Curve.Size.BOXZERO.bytes(), 80); assert (msg.size() == 200); cnNonce++; return 0; } private int processWelcome(Msg msg) { if (msg.size() != 168) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY); return ZError.EPROTO; } ByteBuffer welcomeNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer welcomePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128); ByteBuffer welcomeBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144); // Open Box [S' + cookie](C'->S) welcomeBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(welcomeBox, 24, 144); welcomeNonce.put("WELCOME-".getBytes(ZMQ.CHARSET)); msg.transfer(welcomeNonce, 8, 16); int rc = cryptoBox.open(welcomePlaintext, welcomeBox, welcomeBox.capacity(), welcomeNonce, serverKey, cnSecret); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } welcomePlaintext.position(Curve.Size.ZERO.bytes()); welcomePlaintext.get(cnServer); welcomePlaintext.get(cnCookie); // Message independent precomputation rc = cryptoBox.beforenm(cnPrecom, cnServer, cnSecret); assert (rc == 0); state = State.SEND_INITIATE; return 0; } private int produceInitiate(Msg msg) { ByteBuffer vouchNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer vouchPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer vouchBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); // Create vouch = Box [C',S](C->S') vouchPlaintext.position(Curve.Size.ZERO.bytes()); vouchPlaintext.put(cnPublic); vouchPlaintext.put(serverKey); vouchNonce.put("VOUCH---".getBytes(ZMQ.CHARSET)); vouchNonce.put(cryptoBox.random(16)); int rc = cryptoBox.box(vouchBox, vouchPlaintext, vouchPlaintext.capacity(), vouchNonce, cnServer, secretKey); if (rc == -1) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return -1; } // Assume here that metadata is limited to 256 bytes ByteBuffer initiateNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer initiatePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128 + 256); ByteBuffer initiateBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144 + 256); // Create Box [C + vouch + metadata](C'->S') initiatePlaintext.position(Curve.Size.ZERO.bytes()); initiatePlaintext.put(publicKey); vouchNonce.limit(16 + 8).position(8); initiatePlaintext.put(vouchNonce); vouchBox.limit(Curve.Size.BOXZERO.bytes() + 80).position(Curve.Size.BOXZERO.bytes()); initiatePlaintext.put(vouchBox); // Metadata starts after vouch // Add socket type property String socketType = socketType(); addProperty(initiatePlaintext, SOCKET_TYPE, socketType); // Add identity property if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) { addProperty(initiatePlaintext, IDENTITY, options.identity); } int mlen = initiatePlaintext.position(); initiateNonce.put("CurveZMQINITIATE".getBytes(ZMQ.CHARSET)); Wire.putUInt64(initiateNonce, cnNonce); rc = cryptoBox.box(initiateBox, initiatePlaintext, mlen, initiateNonce, cnServer, cnSecret); if (rc == -1) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return -1; } msg.putShortString("INITIATE"); // Cookie provided by the server in the WELCOME command msg.put(cnCookie); // Short nonce, prefixed by "CurveZMQINITIATE" msg.put(initiateNonce, 16, 8); // Box [C + vouch + metadata](C'->S') msg.put(initiateBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes()); assert (msg.size() == 113 + mlen - Curve.Size.BOXZERO.bytes()); cnNonce++; return 0; } private int processReady(Msg msg) { if (msg.size() < 30) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY); return ZError.EPROTO; } int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 14; ByteBuffer readyNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer readyPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 256); ByteBuffer readyBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 16 + 256); readyBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(readyBox, 14, clen - Curve.Size.BOXZERO.bytes()); readyNonce.put("CurveZMQREADY---".getBytes(ZMQ.CHARSET)); msg.transfer(readyNonce, 6, 8); cnPeerNonce = msg.getLong(6); int rc = cryptoBox.openAfternm(readyPlaintext, readyBox, clen, readyNonce, cnPrecom); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } readyPlaintext.limit(clen); rc = parseMetadata(readyPlaintext, Curve.Size.ZERO.bytes(), false); if (rc == 0) { state = State.CONNECTED; } return rc; } private int processError(Msg msg) { if (state != State.EXPECT_WELCOME && state != State.EXPECT_READY) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } state = State.ERROR_RECEIVED; return parseErrorMessage(msg); } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/CurveServerMechanism.java000066400000000000000000000447301455771126300267420ustar00rootroot00000000000000package zmq.io.mechanism.curve; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import java.nio.ByteBuffer; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; import zmq.io.mechanism.Mechanisms; import zmq.io.net.Address; import zmq.util.Errno; import zmq.util.Wire; public class CurveServerMechanism extends Mechanism { private enum State { EXPECT_HELLO, SEND_WELCOME, EXPECT_INITIATE, EXPECT_ZAP_REPLY, SEND_READY, SEND_ERROR, ERROR_SENT, CONNECTED } private long cnNonce; private long cnPeerNonce; // Our secret key (s) private final byte[] secretKey; // Our short-term public key (S') private final byte[] cnPublic; // Our short-term secret key (s') private final byte[] cnSecret; // Client's short-term public key (C') private final byte[] cnClient = new byte[Curve.Size.PUBLICKEY.bytes()]; // Key used to produce cookie private byte[] cookieKey; // Intermediary buffer used to speed up boxing and unboxing. private final byte[] cnPrecom = new byte[Curve.Size.BEFORENM.bytes()]; private State state; private final Curve cryptoBox; private final Errno errno; public CurveServerMechanism(SessionBase session, Address peerAddress, Options options) { super(session, peerAddress, options); this.state = State.EXPECT_HELLO; cnNonce = 1; cnPeerNonce = 1; secretKey = options.curveSecretKey; assert (secretKey != null && secretKey.length == Curve.Size.SECRETKEY.bytes()); cryptoBox = new Curve(); // Generate short-term key pair byte[][] keys = cryptoBox.keypair(); assert (keys != null && keys.length == 2); cnPublic = keys[0]; assert (cnPublic != null && cnPublic.length == Curve.Size.PUBLICKEY.bytes()); cnSecret = keys[1]; assert (cnSecret != null && cnSecret.length == Curve.Size.SECRETKEY.bytes()); errno = options.errno; } @Override public int nextHandshakeCommand(Msg msg) { int rc; switch (state) { case SEND_WELCOME: rc = produceWelcome(msg); if (rc == 0) { state = State.EXPECT_INITIATE; } break; case SEND_READY: rc = produceReady(msg); if (rc == 0) { state = State.CONNECTED; } break; case SEND_ERROR: rc = produceError(msg); if (rc == 0) { state = State.ERROR_SENT; } break; default: rc = ZError.EAGAIN; break; } return rc; } @Override public int processHandshakeCommand(Msg msg) { int rc; switch (state) { case EXPECT_HELLO: rc = processHello(msg); break; case EXPECT_INITIATE: rc = processInitiate(msg); break; default: session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED); rc = ZError.EPROTO; break; } return rc; } @Override public Msg encode(Msg msg) { assert (state == State.CONNECTED); byte flags = 0; if (msg.hasMore()) { flags |= 0x01; } if (msg.isCommand()) { flags |= 0x02; } ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); messageNonce.put("CurveZMQMESSAGES".getBytes(ZMQ.CHARSET)); Wire.putUInt64(messageNonce, cnNonce); int mlen = Curve.Size.ZERO.bytes() + 1 + msg.size(); ByteBuffer messagePlaintext = ByteBuffer.allocate(mlen); messagePlaintext.put(Curve.Size.ZERO.bytes(), flags); messagePlaintext.position(Curve.Size.ZERO.bytes() + 1); msg.transfer(messagePlaintext, 0, msg.size()); ByteBuffer messageBox = ByteBuffer.allocate(mlen); int rc = cryptoBox.afternm(messageBox, messagePlaintext, mlen, messageNonce, cnPrecom); assert (rc == 0); Msg encoded = new Msg(16 + mlen - Curve.Size.BOXZERO.bytes()); encoded.putShortString("MESSAGE"); encoded.put(messageNonce, 16, 8); encoded.put(messageBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes()); cnNonce++; return encoded; } @Override public Msg decode(Msg msg) { assert (state == State.CONNECTED); if (!compare(msg, "MESSAGE", true)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); errno.set(ZError.EPROTO); return null; } if (msg.size() < 33) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE); errno.set(ZError.EPROTO); return null; } ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); messageNonce.put("CurveZMQMESSAGEC".getBytes(ZMQ.CHARSET)); msg.transfer(messageNonce, 8, 8); long nonce = msg.getLong(8); if (nonce <= cnPeerNonce) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE); errno.set(ZError.EPROTO); return null; } cnPeerNonce = nonce; int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 16; ByteBuffer messagePlaintext = ByteBuffer.allocate(clen); ByteBuffer messageBox = ByteBuffer.allocate(clen); messageBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(messageBox, 16, msg.size() - 16); int rc = cryptoBox.openAfternm(messagePlaintext, messageBox, clen, messageNonce, cnPrecom); if (rc == 0) { Msg decoded = new Msg(clen - 1 - Curve.Size.ZERO.bytes()); byte flags = messagePlaintext.get(Curve.Size.ZERO.bytes()); if ((flags & 0x01) != 0) { decoded.setFlags(Msg.MORE); } if ((flags & 0x02) != 0) { decoded.setFlags(Msg.COMMAND); } messagePlaintext.position(Curve.Size.ZERO.bytes() + 1); decoded.put(messagePlaintext); return decoded; } else { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); errno.set(ZError.EPROTO); return null; } } @Override public int zapMsgAvailable() { if (state != State.EXPECT_ZAP_REPLY) { return ZError.EFSM; } int rc = receiveAndProcessZapReply(); if (rc == 0) { state = "200".equals(statusCode) ? State.SEND_READY : State.SEND_ERROR; } return rc; } @Override public Status status() { if (state == State.CONNECTED) { return Status.READY; } else if (state == State.ERROR_SENT) { return Status.ERROR; } else { return Status.HANDSHAKING; } } private int processHello(Msg msg) { if (!compare(msg, "HELLO", true)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } if (msg.size() != 200) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); return ZError.EPROTO; } byte major = msg.get(6); byte minor = msg.get(7); if (major != 1 || minor != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO); return ZError.EPROTO; } // Save client's short-term public key (C') msg.getBytes(80, cnClient, 0, Curve.Size.PUBLICKEY.bytes()); ByteBuffer helloNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer helloPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer helloBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); helloNonce.put("CurveZMQHELLO---".getBytes(ZMQ.CHARSET)); msg.transfer(helloNonce, 112, 8); cnPeerNonce = msg.getLong(112); helloBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(helloBox, 120, 80); // Open Box [64 * %x0](C'->S) int rc = cryptoBox.open(helloPlaintext, helloBox, helloBox.capacity(), helloNonce, cnClient, secretKey); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); state = State.SEND_ERROR; statusCode = null; return 0; } state = State.SEND_WELCOME; return 0; } private int produceWelcome(Msg msg) { ByteBuffer cookieNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer cookiePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer cookieCiphertext = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); // Create full nonce for encryption // 8-byte prefix plus 16-byte random nonce cookieNonce.put("COOKIE--".getBytes(ZMQ.CHARSET)); cookieNonce.put(cryptoBox.random(16)); // Generate cookie = Box [C' + s'](t) cookiePlaintext.position(Curve.Size.ZERO.bytes()); cookiePlaintext.put(cnClient); cookiePlaintext.put(cnSecret); // Generate fresh cookie key cookieKey = cryptoBox.random(Curve.Size.KEY.bytes()); // Encrypt using symmetric cookie key int rc = cryptoBox .secretbox(cookieCiphertext, cookiePlaintext, cookiePlaintext.capacity(), cookieNonce, cookieKey); assert (rc == 0); ByteBuffer welcomeNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer welcomePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128); ByteBuffer welcomeCiphertext = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144); // Create full nonce for encryption // 8-byte prefix plus 16-byte random nonce welcomeNonce.put("WELCOME-".getBytes(ZMQ.CHARSET)); welcomeNonce.put(cryptoBox.random(Curve.Size.NONCE.bytes() - 8)); // Create 144-byte Box [S' + cookie](S->C') welcomePlaintext.position(Curve.Size.ZERO.bytes()); welcomePlaintext.put(cnPublic); cookieNonce.limit(16 + 8).position(8); welcomePlaintext.put(cookieNonce); cookieCiphertext.limit(Curve.Size.BOXZERO.bytes() + 80).position(Curve.Size.BOXZERO.bytes()); welcomePlaintext.put(cookieCiphertext); rc = cryptoBox.box( welcomeCiphertext, welcomePlaintext, welcomePlaintext.capacity(), welcomeNonce, cnClient, secretKey); if (rc == -1) { return -1; } msg.putShortString("WELCOME"); msg.put(welcomeNonce, 8, 16); msg.put(welcomeCiphertext, Curve.Size.BOXZERO.bytes(), 144); assert (msg.size() == 168); return 0; } private int processInitiate(Msg msg) { if (!compare(msg, "INITIATE", true)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } if (msg.size() < 257) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE); return ZError.EPROTO; } ByteBuffer cookieNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer cookiePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer cookieBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); // Open Box [C' + s'](t) cookieBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(cookieBox, 25, 80); cookieNonce.put("COOKIE--".getBytes(ZMQ.CHARSET)); msg.transfer(cookieNonce, 9, 16); int rc = cryptoBox.secretboxOpen(cookiePlaintext, cookieBox, cookieBox.capacity(), cookieNonce, cookieKey); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } // Check cookie plain text is as expected [C' + s'] if (!compare(cookiePlaintext, cnClient, Curve.Size.ZERO.bytes(), 32) || !compare(cookiePlaintext, cnSecret, Curve.Size.ZERO.bytes() + 32, 32)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } int clen = msg.size() - 113 + Curve.Size.BOXZERO.bytes(); ByteBuffer initiateNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer initiatePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128 + 256); ByteBuffer initiateBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144 + 256); // Open Box [C + vouch + metadata](C'->S') initiateBox.position(Curve.Size.BOXZERO.bytes()); msg.transfer(initiateBox, 113, clen - Curve.Size.BOXZERO.bytes()); initiateNonce.put("CurveZMQINITIATE".getBytes(ZMQ.CHARSET)); msg.transfer(initiateNonce, 105, 8); cnPeerNonce = msg.getLong(105); rc = cryptoBox.open(initiatePlaintext, initiateBox, clen, initiateNonce, cnClient, cnSecret); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } byte[] clientKey = new byte[128 + 256]; initiatePlaintext.position(Curve.Size.ZERO.bytes()); initiatePlaintext.get(clientKey); ByteBuffer vouchNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer vouchPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64); ByteBuffer vouchBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80); // Open Box Box [C',S](C->S') and check contents vouchBox.position(Curve.Size.BOXZERO.bytes()); initiatePlaintext.limit(Curve.Size.ZERO.bytes() + 48 + 80).position(Curve.Size.ZERO.bytes() + 48); vouchBox.put(initiatePlaintext); vouchNonce.put("VOUCH---".getBytes(ZMQ.CHARSET)); initiatePlaintext.limit(Curve.Size.ZERO.bytes() + 32 + 16).position(Curve.Size.ZERO.bytes() + 32); vouchNonce.put(initiatePlaintext); rc = cryptoBox.open(vouchPlaintext, vouchBox, vouchBox.capacity(), vouchNonce, clientKey, cnSecret); if (rc != 0) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC); return ZError.EPROTO; } // What we decrypted must be the client's short-term public key if (!compare(vouchPlaintext, cnClient, Curve.Size.ZERO.bytes(), 32)) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE); return ZError.EPROTO; } // Precompute connection secret from client key rc = cryptoBox.beforenm(cnPrecom, cnClient, cnSecret); assert (rc == 0); // Use ZAP protocol (RFC 27) to authenticate the user. rc = session.zapConnect(); if (rc == 0) { sendZapRequest(clientKey); rc = receiveAndProcessZapReply(); if (rc == 0) { state = "200".equals(statusCode) ? State.SEND_READY : State.SEND_ERROR; } else if (rc == ZError.EAGAIN) { state = State.EXPECT_ZAP_REPLY; } else { return -1; } } else { state = State.SEND_READY; } initiatePlaintext.position(0); initiatePlaintext.limit(clen); return parseMetadata(initiatePlaintext, Curve.Size.ZERO.bytes() + 128, false); } private int produceReady(Msg msg) { ByteBuffer readyNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes()); ByteBuffer readyPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 256); ByteBuffer readyBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 16 + 256); // Create Box [metadata](S'->C') readyPlaintext.position(Curve.Size.ZERO.bytes()); // Add socket type property String socketType = socketType(); addProperty(readyPlaintext, SOCKET_TYPE, socketType); // Add identity property if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) { addProperty(readyPlaintext, IDENTITY, options.identity); } int mlen = readyPlaintext.position(); readyNonce.put("CurveZMQREADY---".getBytes(ZMQ.CHARSET)); Wire.putUInt64(readyNonce, cnNonce); int rc = cryptoBox.afternm(readyBox, readyPlaintext, mlen, readyNonce, cnPrecom); assert (rc == 0); msg.putShortString("READY"); // Short nonce, prefixed by "CurveZMQREADY---" msg.put(readyNonce, 16, 8); // Box [metadata](S'->C') msg.put(readyBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes()); assert (msg.size() == 14 + mlen - Curve.Size.BOXZERO.bytes()); cnNonce++; return 0; } private int produceError(Msg msg) { assert (statusCode == null || statusCode.length() == 3); msg.putShortString("ERROR"); if (statusCode != null) { msg.putShortString(statusCode); } else { msg.putShortString(""); } return 0; } private void sendZapRequest(byte[] key) { sendZapRequest(Mechanisms.CURVE, true); // Credentials frame Msg msg = new Msg(Curve.Size.PUBLICKEY.bytes()); msg.put(key, 0, Curve.Size.PUBLICKEY.bytes()); boolean rc = session.writeZapMsg(msg); assert (rc); } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/000077500000000000000000000000001455771126300221315ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/GssapiClientMechanism.java000066400000000000000000000014001455771126300272010ustar00rootroot00000000000000package zmq.io.mechanism.gssapi; import zmq.Msg; import zmq.Options; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; // TODO V4 implement GSSAPI public class GssapiClientMechanism extends Mechanism { public GssapiClientMechanism(SessionBase session, Options options) { super(session, null, options); throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented"); } @Override public Status status() { return null; } @Override public int zapMsgAvailable() { return 0; } @Override public int processHandshakeCommand(Msg msg) { return 0; } @Override public int nextHandshakeCommand(Msg msg) { return 0; } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/GssapiServerMechanism.java000066400000000000000000000014671455771126300272460ustar00rootroot00000000000000package zmq.io.mechanism.gssapi; import zmq.Msg; import zmq.Options; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; import zmq.io.net.Address; // TODO V4 implement GSSAPI public class GssapiServerMechanism extends Mechanism { public GssapiServerMechanism(SessionBase session, Address peerAddress, Options options) { super(session, peerAddress, options); throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented"); } @Override public Status status() { return null; } @Override public int zapMsgAvailable() { return 0; } @Override public int processHandshakeCommand(Msg msg) { return 0; } @Override public int nextHandshakeCommand(Msg msg) { return 0; } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/000077500000000000000000000000001455771126300217465ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/PlainClientMechanism.java000066400000000000000000000100501455771126300266340ustar00rootroot00000000000000package zmq.io.mechanism.plain; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; public class PlainClientMechanism extends Mechanism { private enum State { SENDING_HELLO, WAITING_FOR_WELCOME, SENDING_INITIATE, WAITING_FOR_READY, ERROR_COMMAND_RECEIVED, READY } private State state; public PlainClientMechanism(SessionBase session, Options options) { super(session, null, options); this.state = State.SENDING_HELLO; } @Override public int nextHandshakeCommand(Msg msg) { int rc; switch (state) { case SENDING_HELLO: rc = produceHello(msg); if (rc == 0) { state = State.WAITING_FOR_WELCOME; } break; case SENDING_INITIATE: rc = produceInitiate(msg); if (rc == 0) { state = State.WAITING_FOR_READY; } break; default: rc = ZError.EAGAIN; break; } return rc; } @Override public int processHandshakeCommand(Msg msg) { int rc; int dataSize = msg.size(); if (dataSize >= 8 && compare(msg, "WELCOME", true)) { rc = processWelcome(msg); } else if (dataSize >= 6 && compare(msg, "READY", true)) { rc = processReady(msg); } else if (dataSize >= 6 && compare(msg, "ERROR", true)) { rc = processError(msg); } else { // Temporary support for security debugging System.out.println("PLAIN Client I: invalid handshake command"); rc = ZError.EPROTO; } return rc; } @Override public Status status() { if (state == State.READY) { return Status.READY; } else if (state == State.ERROR_COMMAND_RECEIVED) { return Status.ERROR; } else { return Status.HANDSHAKING; } } @Override public int zapMsgAvailable() { return 0; } private int produceHello(Msg msg) { String plainUsername = options.plainUsername; assert (plainUsername.length() < 256); String plainPassword = options.plainPassword; assert (plainPassword.length() < 256); msg.putShortString("HELLO"); msg.putShortString(plainUsername); msg.putShortString(plainPassword); return 0; } private int processWelcome(Msg msg) { if (state != State.WAITING_FOR_WELCOME) { return ZError.EPROTO; } if (msg.size() != 8) { return ZError.EPROTO; } state = State.SENDING_INITIATE; return 0; } private int produceInitiate(Msg msg) { // Add mechanism string msg.putShortString("INITIATE"); // Add socket type property String socketType = socketType(); addProperty(msg, SOCKET_TYPE, socketType); // Add identity property if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) { addProperty(msg, IDENTITY, options.identity); } return 0; } private int processReady(Msg msg) { if (state != State.WAITING_FOR_READY) { return ZError.EPROTO; } int rc = parseMetadata(msg, 6, false); if (rc == 0) { state = State.READY; } return rc; } private int processError(Msg msg) { if (state != State.WAITING_FOR_WELCOME && state != State.WAITING_FOR_READY) { session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND); return ZError.EPROTO; } state = State.ERROR_COMMAND_RECEIVED; return parseErrorMessage(msg); } } jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/PlainServerMechanism.java000066400000000000000000000134141455771126300266730ustar00rootroot00000000000000package zmq.io.mechanism.plain; import static zmq.io.Metadata.IDENTITY; import static zmq.io.Metadata.SOCKET_TYPE; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; import zmq.io.SessionBase; import zmq.io.mechanism.Mechanism; import zmq.io.mechanism.Mechanisms; import zmq.io.net.Address; public class PlainServerMechanism extends Mechanism { private enum State { WAITING_FOR_HELLO, SENDING_WELCOME, WAITING_FOR_INITIATE, SENDING_READY, WAITING_FOR_ZAP_REPLY, SENDING_ERROR, ERROR_COMMAND_SENT, READY } private State state; public PlainServerMechanism(SessionBase session, Address peerAddress, Options options) { super(session, peerAddress, options); this.state = State.WAITING_FOR_HELLO; } @Override public int nextHandshakeCommand(Msg msg) { int rc; switch (state) { case SENDING_WELCOME: rc = produceWelcome(msg); if (rc == 0) { state = State.WAITING_FOR_INITIATE; } break; case SENDING_READY: rc = produceReady(msg); if (rc == 0) { state = State.READY; } break; case SENDING_ERROR: rc = produceError(msg); if (rc == 0) { state = State.ERROR_COMMAND_SENT; } break; default: rc = ZError.EAGAIN; break; } return rc; } @Override public int processHandshakeCommand(Msg msg) { int rc; switch (state) { case WAITING_FOR_HELLO: rc = produceHello(msg); break; case WAITING_FOR_INITIATE: rc = produceInitiate(msg); break; default: rc = ZError.EPROTO; break; } return rc; } @Override public Status status() { if (state == State.READY) { return Status.READY; } else if (state == State.ERROR_COMMAND_SENT) { return Status.ERROR; } else { return Status.HANDSHAKING; } } @Override public int zapMsgAvailable() { if (state != State.WAITING_FOR_ZAP_REPLY) { return ZError.EFSM; } int rc = receiveAndProcessZapReply(); if (rc == 0) { state = "200".equals(statusCode) ? State.SENDING_WELCOME : State.SENDING_ERROR; } return rc; } private int produceHello(Msg msg) { int bytesLeft = msg.size(); int index = 0; if (bytesLeft < 6 || !compare(msg, "HELLO", true)) { return ZError.EPROTO; } bytesLeft -= 6; index += 6; if (bytesLeft < 1) { return ZError.EPROTO; } byte length = msg.get(index); bytesLeft -= 1; if (bytesLeft < length) { return ZError.EPROTO; } byte[] tmp = new byte[length]; index += 1; msg.getBytes(index, tmp, 0, length); byte[] username = tmp; bytesLeft -= length; index += length; length = msg.get(index); bytesLeft -= 1; if (bytesLeft < length) { return ZError.EPROTO; } tmp = new byte[length]; index += 1; msg.getBytes(index, tmp, 0, length); byte[] password = tmp; bytesLeft -= length; // index += length; if (bytesLeft > 0) { return ZError.EPROTO; } // Use ZAP protocol (RFC 27) to authenticate the user. int rc = session.zapConnect(); if (rc == 0) { sendZapRequest(username, password); rc = receiveAndProcessZapReply(); if (rc == 0) { state = "200".equals(statusCode) ? State.SENDING_WELCOME : State.SENDING_ERROR; } else if (rc == ZError.EAGAIN) { state = State.WAITING_FOR_ZAP_REPLY; } else { return -1; } } else { state = State.SENDING_WELCOME; } return 0; } private int produceWelcome(Msg msg) { msg.putShortString("WELCOME"); return 0; } private int produceInitiate(Msg msg) { int bytesLeft = msg.size(); if (bytesLeft < 9 || !compare(msg, "INITIATE", true)) { return ZError.EPROTO; } int rc = parseMetadata(msg, 9, false); if (rc == 0) { state = State.SENDING_READY; } return rc; } private int produceReady(Msg msg) { // Add command name msg.putShortString("READY"); // Add socket type property String socketType = socketType(); addProperty(msg, SOCKET_TYPE, socketType); // Add identity property if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) { addProperty(msg, IDENTITY, options.identity); } return 0; } private int produceError(Msg msg) { assert (statusCode != null && statusCode.length() == 3); msg.putShortString("ERROR"); msg.putShortString(statusCode); return 0; } private void sendZapRequest(byte[] username, byte[] password) { sendZapRequest(Mechanisms.PLAIN, true); // Username frame Msg msg = new Msg(username.length); msg.setFlags(Msg.MORE); msg.put(username); boolean rc = session.writeZapMsg(msg); assert (rc); // Password frame msg = new Msg(password.length); msg.put(password); rc = session.writeZapMsg(msg); assert (rc); } } jeromq-0.6.0/src/main/java/zmq/io/net/000077500000000000000000000000001455771126300174655ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/Address.java000066400000000000000000000051301455771126300217140ustar00rootroot00000000000000package zmq.io.net; import java.net.InetSocketAddress; import java.net.SocketAddress; public class Address { public interface IZAddress { ProtocolFamily family(); String toString(int port); InetSocketAddress resolve(String name, boolean ipv6, boolean local); SocketAddress address(); SocketAddress sourceAddress(); } private final NetProtocol protocol; private final String address; private IZAddress resolved; /** * @param protocol * @param address * @throws IllegalArgumentException if the protocol name can be matched to an actual supported protocol */ @Deprecated public Address(final String protocol, final String address) { this.protocol = NetProtocol.getProtocol(protocol); this.address = address; resolved = null; } /** * @param protocol * @param address */ public Address(final NetProtocol protocol, final String address) { this.protocol = protocol; this.address = address; resolved = null; } /** * @param socketAddress * @throws IllegalArgumentException if the SocketChannel is not an IP socket address */ public Address(SocketAddress socketAddress) { if (socketAddress instanceof InetSocketAddress) { InetSocketAddress sockAddr = (InetSocketAddress) socketAddress; this.address = sockAddr.getAddress().getHostAddress() + ":" + sockAddr.getPort(); protocol = NetProtocol.tcp; resolved = null; } else { throw new IllegalArgumentException("Not a IP socket address"); } } @Override public String toString() { if (isResolved()) { return resolved.toString(); } else if (protocol != null && !address.isEmpty()) { return protocol.name() + "://" + address; } else { return ""; } } public NetProtocol protocol() { return protocol; } public String address() { return address; } public String host() { final int portDelimiter = address.lastIndexOf(':'); if (portDelimiter > 0) { return address.substring(0, portDelimiter); } return address; } public IZAddress resolved() { return resolved; } public boolean isResolved() { return resolved != null; } public IZAddress resolve(boolean ipv6) { resolved = protocol.zresolve(address, ipv6); return resolved; } } jeromq-0.6.0/src/main/java/zmq/io/net/Listener.java000066400000000000000000000010341455771126300221130ustar00rootroot00000000000000package zmq.io.net; import zmq.Options; import zmq.Own; import zmq.SocketBase; import zmq.io.IOThread; import zmq.poll.IPollEvents; public abstract class Listener extends Own implements IPollEvents { // Socket the listener belongs to. protected final SocketBase socket; protected Listener(IOThread ioThread, SocketBase socket, final Options options) { super(ioThread, options); this.socket = socket; } public abstract boolean setAddress(String addr); public abstract String getAddress(); } jeromq-0.6.0/src/main/java/zmq/io/net/NetProtocol.java000066400000000000000000000072531455771126300226070ustar00rootroot00000000000000package zmq.io.net; import java.util.Arrays; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; import zmq.Options; import zmq.SocketBase; import zmq.io.IOThread; import zmq.io.net.Address.IZAddress; import zmq.io.net.ipc.IpcAddress; import zmq.io.net.ipc.IpcListener; import zmq.io.net.tcp.TcpAddress; import zmq.io.net.tcp.TcpListener; import zmq.io.net.tipc.TipcListener; import zmq.socket.Sockets; public enum NetProtocol { inproc(true, false, false), ipc(true, false, false) { @Override public Listener getListener(IOThread ioThread, SocketBase socket, Options options) { return new IpcListener(ioThread, socket, options); } @Override public void resolve(Address paddr, boolean ipv6) { paddr.resolve(ipv6); } @Override public IZAddress zresolve(String addr, boolean ipv6) { return new IpcAddress(addr); } }, tcp(true, false, false) { @Override public Listener getListener(IOThread ioThread, SocketBase socket, Options options) { return new TcpListener(ioThread, socket, options); } @Override public void resolve(Address paddr, boolean ipv6) { paddr.resolve(ipv6); } @Override public IZAddress zresolve(String addr, boolean ipv6) { return new TcpAddress(addr, ipv6); } }, // PGM does not support subscription forwarding; ask for all data to be // sent to this pipe. (same for NORM, currently?) pgm(false, true, true, Sockets.PUB, Sockets.SUB, Sockets.XPUB, Sockets.XPUB), epgm(false, true, true, Sockets.PUB, Sockets.SUB, Sockets.XPUB, Sockets.XPUB), tipc(false, false, false) { @Override public Listener getListener(IOThread ioThread, SocketBase socket, Options options) { return new TipcListener(ioThread, socket, options); } @Override public void resolve(Address paddr, boolean ipv6) { paddr.resolve(ipv6); } }, norm(false, true, true); public final boolean valid; public final boolean subscribe2all; public final boolean isMulticast; private final Set compatibles; NetProtocol(boolean implemented, boolean subscribe2all, boolean isMulticast, Sockets... compatibles) { valid = implemented; this.compatibles = Arrays.stream(compatibles).map(Sockets::ordinal).collect(Collectors.toSet()); this.subscribe2all = subscribe2all; this.isMulticast = isMulticast; } /** * @param protocol name * @throws IllegalArgumentException if the protocol name can be matched to an actual supported protocol * @return */ public static NetProtocol getProtocol(String protocol) { try { return valueOf(protocol.toLowerCase(Locale.ENGLISH)); } catch (NullPointerException | IllegalArgumentException e) { throw new IllegalArgumentException("Unknown protocol: \"" + protocol + "\""); } } public final boolean compatible(int type) { return compatibles.isEmpty() || compatibles.contains(type); } public Listener getListener(IOThread ioThread, SocketBase socket, Options options) { return null; } public void resolve(Address paddr, boolean ipv6) { // TODO V4 init address for pgm & epgm } public IZAddress zresolve(String addr, boolean ipv6) { return null; } } jeromq-0.6.0/src/main/java/zmq/io/net/ProtocolFamily.java000066400000000000000000000002511455771126300232710ustar00rootroot00000000000000package zmq.io.net; /** * Replacement of ProtocolFamily from SDK so it can be used in Android environments. */ public interface ProtocolFamily { String name(); } jeromq-0.6.0/src/main/java/zmq/io/net/SelectorProviderChooser.java000066400000000000000000000007021455771126300251450ustar00rootroot00000000000000package zmq.io.net; import java.nio.channels.spi.SelectorProvider; import zmq.Options; /** * By implementing this class, it's possible to change the kind of channel used in tcp connections.

* It allows to easily wrap ZMQ socket in custom socket for TLS protection or other kind of trick. * * @author Fabrice Bacchella * */ public interface SelectorProviderChooser { SelectorProvider choose(Address.IZAddress addr, Options options); } jeromq-0.6.0/src/main/java/zmq/io/net/StandardProtocolFamily.java000066400000000000000000000003171455771126300247550ustar00rootroot00000000000000package zmq.io.net; /** * Replacement of StandardProtocolFamily from SDK so it can be used in Android environments. */ public enum StandardProtocolFamily implements ProtocolFamily { INET, INET6 } jeromq-0.6.0/src/main/java/zmq/io/net/ipc/000077500000000000000000000000001455771126300202405ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcAddress.java000066400000000000000000000066261455771126300231360ustar00rootroot00000000000000package zmq.io.net.ipc; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; import java.util.Enumeration; import zmq.ZMQ; import zmq.io.net.Address; import zmq.io.net.ProtocolFamily; import zmq.io.net.StandardProtocolFamily; import zmq.io.net.tcp.TcpAddress; import zmq.util.Utils; public class IpcAddress implements Address.IZAddress { public static class IpcAddressMask extends TcpAddress { public IpcAddressMask(String addr, boolean ipv6) { super(addr, ipv6); } public boolean matchAddress(SocketAddress addr) { return address().equals(addr); } } private String name; private final InetSocketAddress address; private final SocketAddress sourceAddress; public IpcAddress(String addr) { String[] strings = addr.split(";"); address = resolve(strings[0], ZMQ.PREFER_IPV6, true); if (strings.length == 2 && !"".equals(strings[1])) { sourceAddress = resolve(strings[1], ZMQ.PREFER_IPV6, true); } else { sourceAddress = null; } } @Override public String toString() { if (name == null) { return ""; } return "ipc://" + name; } @Override public String toString(int port) { if ("*".equals(name)) { String suffix = Utils.unhash(port - 10000); return "ipc://" + suffix; } return toString(); } @Override public InetSocketAddress resolve(String name, boolean ipv6, boolean loopback) { this.name = name; int hash = name.hashCode(); if ("*".equals(name)) { hash = 0; } else { if (hash < 0) { hash = -hash; } hash = hash % 55536; hash += 10000; } return new InetSocketAddress(findAddress(ipv6, loopback), hash); } @Override public SocketAddress address() { return address; } @Override public ProtocolFamily family() { return StandardProtocolFamily.INET; } @Override public SocketAddress sourceAddress() { return sourceAddress; } private InetAddress findAddress(boolean ipv6, boolean loopback) { Class addressClass = ipv6 ? Inet6Address.class : Inet4Address.class; try { for (Enumeration interfaces = NetworkInterface .getNetworkInterfaces(); interfaces.hasMoreElements(); ) { NetworkInterface net = interfaces.nextElement(); for (Enumeration addresses = net.getInetAddresses(); addresses.hasMoreElements(); ) { InetAddress inetAddress = addresses.nextElement(); if (inetAddress.isLoopbackAddress() == loopback && addressClass.isInstance(inetAddress)) { return inetAddress; } } } } catch (SocketException e) { throw new IllegalArgumentException(e); } throw new IllegalArgumentException("no address found " + (ipv6 ? "IPV6" : "IPV4") + (loopback ? "local" : "")); } } jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcConnecter.java000066400000000000000000000006171455771126300234630ustar00rootroot00000000000000package zmq.io.net.ipc; import zmq.Options; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.io.net.tcp.TcpConnecter; public class IpcConnecter extends TcpConnecter { public IpcConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr, boolean wait) { super(ioThread, session, options, addr, wait); } } jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcListener.java000066400000000000000000000016421455771126300233270ustar00rootroot00000000000000package zmq.io.net.ipc; import java.net.InetSocketAddress; import zmq.Options; import zmq.SocketBase; import zmq.io.IOThread; import zmq.io.net.tcp.TcpListener; // fake Unix domain socket public class IpcListener extends TcpListener { private IpcAddress address; public IpcListener(IOThread ioThread, SocketBase socket, final Options options) { super(ioThread, socket, options); } // Get the bound address for use with wildcards @Override public String getAddress() { if (((InetSocketAddress) address.address()).getPort() == 0) { return address(address); } return address.toString(); } // Set address to listen on. @Override public boolean setAddress(String addr) { address = new IpcAddress(addr); InetSocketAddress sock = (InetSocketAddress) address.address(); return super.setAddress(sock); } } jeromq-0.6.0/src/main/java/zmq/io/net/norm/000077500000000000000000000000001455771126300204405ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/norm/NormEngine.java000066400000000000000000000006701455771126300233470ustar00rootroot00000000000000package zmq.io.net.norm; import zmq.Options; import zmq.io.EngineNotImplemented; import zmq.io.IOThread; import zmq.io.net.Address; // TODO V4 implement NORM engine public class NormEngine extends EngineNotImplemented { public NormEngine(IOThread ioThread, Options options) { throw new UnsupportedOperationException(); } public boolean init(Address addr, boolean b, boolean c) { return false; } } jeromq-0.6.0/src/main/java/zmq/io/net/pgm/000077500000000000000000000000001455771126300202505ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/pgm/PgmReceiver.java000066400000000000000000000006751455771126300233330ustar00rootroot00000000000000package zmq.io.net.pgm; import zmq.Options; import zmq.io.EngineNotImplemented; import zmq.io.IOThread; import zmq.io.net.Address; //TODO V4 implement pgm receiver public class PgmReceiver extends EngineNotImplemented { public PgmReceiver(IOThread ioThread, Options options) { throw new UnsupportedOperationException(); } public boolean init(boolean udpEncapsulation, Address addr) { return false; } } jeromq-0.6.0/src/main/java/zmq/io/net/pgm/PgmSender.java000066400000000000000000000006701455771126300230020ustar00rootroot00000000000000package zmq.io.net.pgm; import zmq.Options; import zmq.io.EngineNotImplemented; import zmq.io.IOThread; import zmq.io.net.Address; // TODO V4 implement pgm sender public class PgmSender extends EngineNotImplemented { public PgmSender(IOThread ioThread, Options options) { throw new UnsupportedOperationException(); } public boolean init(boolean udpEncapsulation, Address addr) { return false; } } jeromq-0.6.0/src/main/java/zmq/io/net/tcp/000077500000000000000000000000001455771126300202535ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/tcp/SocksConnecter.java000066400000000000000000000061251455771126300240450ustar00rootroot00000000000000package zmq.io.net.tcp; import zmq.Options; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.io.net.NetProtocol; // TODO continue socks connecter public class SocksConnecter extends TcpConnecter { private enum Status { UNPLUGGED, WAITING_FOR_RECONNECT_TIME, WAITING_FOR_PROXY_CONNECTION, SENDING_GREETING, WAITING_FOR_CHOICE, SENDING_REQUEST, WAITING_FOR_RESPONSE } Status status; // String representation of endpoint to connect to String endpoint; public SocksConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr, final Address proxyAddr, boolean delayedStart) { super(ioThread, session, options, addr, delayedStart); assert (NetProtocol.tcp.equals(addr.protocol())); endpoint = proxyAddr.toString(); this.status = Status.UNPLUGGED; throw new UnsupportedOperationException("Socks connecter is not implemented"); } @Override protected void processPlug() { if (delayedStart) { startTimer(); } else { initiateConnect(); } } @Override protected void processTerm(int linger) { switch (status) { case UNPLUGGED: break; case WAITING_FOR_RECONNECT_TIME: ioObject.cancelTimer(RECONNECT_TIMER_ID); break; case WAITING_FOR_PROXY_CONNECTION: case SENDING_GREETING: case WAITING_FOR_CHOICE: case SENDING_REQUEST: case WAITING_FOR_RESPONSE: close(); break; default: break; } super.processTerm(linger); } @Override public void inEvent() { assert (status != Status.UNPLUGGED && status != Status.WAITING_FOR_RECONNECT_TIME); super.inEvent(); } @Override public void outEvent() { super.outEvent(); } @Override public void timerEvent(int id) { super.timerEvent(id); } // Internal function to start the actual connection establishment. void initiateConnect() { } int processServerResponse() { return -1; } void parseAddress(String address, String hostname, int port) { } void connectToProxy() { } void error() { } // Internal function to start reconnect timer void startTimer() { } // Internal function to return a reconnect backoff delay. // Will modify the current_reconnect_ivl used for next call // Returns the currently used interval int getNewReconnectIvl() { return -1; } // Open TCP connecting socket. Returns -1 in case of error, // 0 if connect was successfull immediately. Returns -1 with // EAGAIN errno if async connect was launched. int open() { return -1; } // Get the file descriptor of newly created connection. Returns // retired_fd if the connection was unsuccessfull. void checkProxyConnection() { } } jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpAddress.java000066400000000000000000000120761455771126300231600ustar00rootroot00000000000000package zmq.io.net.tcp; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import org.zeromq.ZMQException; import zmq.ZError; import zmq.io.net.Address; import zmq.io.net.ProtocolFamily; import zmq.io.net.StandardProtocolFamily; public class TcpAddress implements Address.IZAddress { public static class TcpAddressMask extends TcpAddress { public TcpAddressMask(String addr, boolean ipv6) { super(addr, ipv6); } public boolean matchAddress(SocketAddress addr) { return address().equals(addr); } } private final InetSocketAddress address; private final SocketAddress sourceAddress; public TcpAddress(String addr, boolean ipv6) { String[] strings = addr.split(";"); address = resolve(strings[0], ipv6, false); if (strings.length == 2 && !"".equals(strings[1])) { sourceAddress = resolve(strings[1], ipv6, false); } else { sourceAddress = null; } } protected TcpAddress(InetSocketAddress address) { this.address = address; sourceAddress = null; } @Override public ProtocolFamily family() { if (address.getAddress() instanceof Inet6Address) { return StandardProtocolFamily.INET6; } return StandardProtocolFamily.INET; } // The opposite to resolve() @Override public String toString() { return toString(address.getPort()); } // The opposite to resolve() @Override public String toString(int port) { if (address == null) { return ""; } int addressPort = address.getPort(); if (addressPort == 0) { addressPort = port; } if (address.getAddress() instanceof Inet6Address) { return "tcp://[" + address.getAddress().getHostAddress() + "]:" + addressPort; } else { return "tcp://" + address.getAddress().getHostAddress() + ":" + addressPort; } } /** * @param name * @param ipv6 * @param local ignored * @return the resolved address * @see zmq.io.net.Address.IZAddress#resolve(java.lang.String, boolean, boolean) */ @Override public InetSocketAddress resolve(String name, boolean ipv6, boolean local) { // Find the ':' at end that separates address from the port number. int delimiter = name.lastIndexOf(':'); if (delimiter < 0) { throw new IllegalArgumentException(name); } // Separate the address/port. String addrStr = name.substring(0, delimiter); String portStr = name.substring(delimiter + 1); // Remove square brackets around the address, if any. if (addrStr.length() >= 2 && addrStr.charAt(0) == '[' && addrStr.charAt(addrStr.length() - 1) == ']') { addrStr = addrStr.substring(1, addrStr.length() - 1); } int port; // Allow 0 specifically, to detect invalid port error in atoi if not if (portStr.equals("*") || portStr.equals("0")) { // Resolve wildcard to 0 to allow autoselection of port port = 0; } else { // Parse the port number (0 is not a valid port). port = Integer.parseInt(portStr); if (port == 0) { throw new IllegalArgumentException(name); } } InetAddress addrNet = null; // '*' as unspecified address is not accepted in Java // '::' for IPv6 is accepted if (addrStr.equals("*")) { addrStr = ipv6 ? "::" : "0.0.0.0"; } try { InetAddress[] addresses = InetAddress.getAllByName(addrStr); if (ipv6) { // prefer IPv6: return the first ipv6 or the first value if not found for (InetAddress addr : addresses) { if (addr instanceof Inet6Address) { addrNet = addr; break; } } if (addrNet == null) { addrNet = addresses[0]; } } else { for (InetAddress addr : addresses) { if (addr instanceof Inet4Address) { addrNet = addr; break; } } } } catch (UnknownHostException e) { throw new ZMQException(e.getMessage(), ZError.EADDRNOTAVAIL, e); } if (addrNet == null) { throw new ZMQException(addrStr + " not found matching IPv4/IPv6 settings", ZError.EADDRNOTAVAIL); } return new InetSocketAddress(addrNet, port); } @Override public SocketAddress address() { return address; } @Override public SocketAddress sourceAddress() { return sourceAddress; } } jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpConnecter.java000066400000000000000000000246711455771126300235170ustar00rootroot00000000000000package zmq.io.net.tcp; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.SocketChannel; import zmq.Options; import zmq.Own; import zmq.SocketBase; import zmq.ZError; import zmq.io.IOObject; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.StreamEngine; import zmq.io.net.Address; import zmq.io.net.StandardProtocolFamily; import zmq.poll.IPollEvents; import zmq.poll.Poller; import zmq.util.Utils; // If 'delay' is true connecter first waits for a while, then starts // connection process. public class TcpConnecter extends Own implements IPollEvents { // ID of the timer used to delay the reconnection. protected static final int RECONNECT_TIMER_ID = 1; protected final IOObject ioObject; // Address to connect to. Owned by session_base_t. private final Address addr; // Underlying socket. private SocketChannel fd; private Poller.Handle handle; // If true, connecter is waiting a while before trying to connect. protected final boolean delayedStart; // True if a timer has been started. private boolean timerStarted; // Reference to the session we belong to. private final SessionBase session; // Current reconnect ivl, updated for backoff strategy private int currentReconnectIvl; // Socket private final SocketBase socket; public TcpConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr, boolean delayedStart) { super(ioThread, options); ioObject = new IOObject(ioThread, this); this.addr = addr; fd = null; this.delayedStart = delayedStart; timerStarted = false; this.session = session; currentReconnectIvl = this.options.reconnectIvl; assert (this.addr != null); // assert (NetProtocol.tcp.equals(this.addr.protocol())); // not always true, as ipc is emulated by tcp socket = session.getSocket(); } @Override protected void destroy() { assert (!timerStarted); assert (handle == null); assert (fd == null); ioObject.unplug(); } @Override protected void processPlug() { ioObject.plug(); if (delayedStart) { addReconnectTimer(); } else { startConnecting(); } } @Override protected void processTerm(int linger) { if (timerStarted) { ioObject.cancelTimer(RECONNECT_TIMER_ID); timerStarted = false; } if (handle != null) { ioObject.removeHandle(handle); handle = null; } if (fd != null) { close(); } super.processTerm(linger); } @Override public void connectEvent() { ioObject.removeHandle(handle); handle = null; SocketChannel channel = connect(); if (channel == null) { // Handle the error condition by attempt to reconnect. close(); addReconnectTimer(); return; } try { TcpUtils.tuneTcpSocket(channel); TcpUtils.tuneTcpKeepalives( channel, options.tcpKeepAlive, options.tcpKeepAliveCnt, options.tcpKeepAliveIdle, options.tcpKeepAliveIntvl); } catch (IOException e) { throw new ZError.IOException(e); } // remember our fd for ZMQ_SRCFD in messages // socket.setFd(channel); // Create the engine object for this connection. StreamEngine engine; try { engine = new StreamEngine(channel, options, addr.toString()); } catch (ZError.InstantiationException e) { // TODO V4 socket.eventConnectDelayed(addr.toString(), -1); return; } this.fd = null; // Attach the engine to the corresponding session object. sendAttach(session, engine); // Shut the connecter down. terminate(); socket.eventConnected(addr.toString(), channel); } @Override public void timerEvent(int id) { assert (id == RECONNECT_TIMER_ID); timerStarted = false; startConnecting(); } // Internal function to start the actual connection establishment. private void startConnecting() { // Open the connecting socket. try { boolean rc = open(); // Connect may succeed in synchronous manner. if (rc) { handle = ioObject.addFd(fd); connectEvent(); } // Connection establishment may be delayed. Poll for its completion. else { handle = ioObject.addFd(fd); ioObject.setPollConnect(handle); socket.eventConnectDelayed(addr.toString(), -1); } } catch (RuntimeException | IOException e) { // Handle any other error condition by eventual reconnect. if (fd != null) { close(); } addReconnectTimer(); } } // Internal function to add a reconnect timer private void addReconnectTimer() { int rcIvl = getNewReconnectIvl(); ioObject.addTimer(rcIvl, RECONNECT_TIMER_ID); // resolve address again to take into account other addresses // besides the failing one (e.g. multiple dns entries). try { addr.resolve(options.ipv6); } catch (Exception ignored) { // This will fail if the network goes away and the // address cannot be resolved for some reason. Try // not to fail as the event loop will quit } socket.eventConnectRetried(addr.toString(), rcIvl); timerStarted = true; } // Internal function to return a reconnect backoff delay. // Will modify the currentReconnectIvl used for next call // Returns the currently used interval private int getNewReconnectIvl() { // The new interval is the current interval + random value. int interval = currentReconnectIvl + (Utils.randomInt() % options.reconnectIvl); // Only change the current reconnect interval if the maximum reconnect // interval was set and if it's larger than the reconnect interval. if (options.reconnectIvlMax > 0 && options.reconnectIvlMax > options.reconnectIvl) { // Calculate the next interval currentReconnectIvl = Math.min(currentReconnectIvl * 2, options.reconnectIvlMax); } return interval; } // Open TCP connecting socket. // Returns true if connect was successful immediately. // Returns false if async connect was launched. private boolean open() throws IOException { assert (fd == null); // Resolve the address if (addr == null) { throw new IOException("Null address"); } addr.resolve(options.ipv6); Address.IZAddress resolved = addr.resolved(); if (resolved == null) { throw new IOException("Address not resolved"); } SocketAddress sa = resolved.address(); if (sa == null) { throw new IOException("Socket address not resolved"); } // Create the socket. if (options.selectorChooser == null) { fd = SocketChannel.open(); } else { fd = options.selectorChooser.choose(resolved, options).openSocketChannel(); } // On some systems, IPv4 mapping in IPv6 sockets is disabled by default. // Switch it on in such cases. // The method enableIpv4Mapping is empty. Still to be written if (resolved.family() == StandardProtocolFamily.INET6) { TcpUtils.enableIpv4Mapping(fd); } // Set the socket to non-blocking mode so that we get async connect(). TcpUtils.unblockSocket(fd); // Set the socket buffer limits for the underlying socket. if (options.sndbuf != 0) { TcpUtils.setTcpSendBuffer(fd, options.sndbuf); } if (options.rcvbuf != 0) { TcpUtils.setTcpReceiveBuffer(fd, options.rcvbuf); } // Set the IP Type-Of-Service priority for this socket if (options.tos != 0) { TcpUtils.setIpTypeOfService(fd, options.tos); } // TODO V4 Set a source address for conversations // Connect to the remote peer. boolean rc; try { rc = fd.connect(sa); if (rc) { // Connect was successful immediately. } else { // Translate error codes indicating asynchronous connect has been // launched to a uniform EINPROGRESS. errno.set(ZError.EINPROGRESS); } } catch (IllegalArgumentException e) { // this will happen if sa is bad. Address validation is not documented but // I've found that IAE is thrown in openjdk as well as on android. throw new IOException(e.getMessage(), e); } return rc; } // Get the file descriptor of newly created connection. Returns // null if the connection was unsuccessful. private SocketChannel connect() { try { // Async connect has finished. Check whether an error occurred boolean finished = fd.finishConnect(); assert (finished); return fd; } catch (IOException e) { return null; } } // Close the connecting socket. protected void close() { assert (fd != null); try { fd.close(); socket.eventClosed(addr.toString(), fd); } catch (IOException e) { socket.eventCloseFailed(addr.toString(), ZError.exccode(e)); } fd = null; } @Override public void inEvent() { // connected but attaching to stream engine is not completed. do nothing } @Override public void outEvent() { // connected but attaching to stream engine is not completed. do nothing } @Override public String toString() { return getClass().getSimpleName() + "[" + options.socketId + "]"; } } jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpListener.java000066400000000000000000000203271455771126300233560ustar00rootroot00000000000000package zmq.io.net.tcp; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Locale; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.io.IOObject; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.StreamEngine; import zmq.io.net.Address.IZAddress; import zmq.io.net.Listener; import zmq.io.net.StandardProtocolFamily; import zmq.poll.Poller; import zmq.socket.Sockets; public class TcpListener extends Listener { private static final boolean isWindows; static { String os = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); isWindows = os.contains("win"); } // Address to listen on. private TcpAddress address; // Underlying socket. private ServerSocketChannel fd; private Poller.Handle handle; // String representation of endpoint to bind to private String endpoint; private final IOObject ioObject; public TcpListener(IOThread ioThread, SocketBase socket, final Options options) { super(ioThread, socket, options); ioObject = new IOObject(ioThread, this); fd = null; } @Override public void destroy() { assert (fd == null); assert (handle == null); ioObject.unplug(); } @Override protected void processPlug() { // Start polling for incoming connections. ioObject.plug(); handle = ioObject.addFd(fd); ioObject.setPollAccept(handle); } @Override protected void processTerm(int linger) { ioObject.removeHandle(handle); handle = null; close(); super.processTerm(linger); } @Override public void acceptEvent() { SocketChannel channel; try { channel = accept(); // If connection was reset by the peer in the meantime, just ignore it. if (channel == null) { socket.eventAcceptFailed(endpoint, ZError.EADDRNOTAVAIL); return; } TcpUtils.tuneTcpSocket(channel); TcpUtils.tuneTcpKeepalives( channel, options.tcpKeepAlive, options.tcpKeepAliveCnt, options.tcpKeepAliveIdle, options.tcpKeepAliveIntvl); } catch (IOException e) { // If connection was reset by the peer in the meantime, just ignore it. // TODO: Handle specific errors like ENFILE/EMFILE etc. socket.eventAcceptFailed(endpoint, ZError.exccode(e)); return; } // remember our fd for ZMQ_SRCFD in messages // socket.setFd(channel); // Create the engine object for this connection. StreamEngine engine; try { engine = new StreamEngine(channel, options, endpoint); } catch (ZError.InstantiationException e) { socket.eventAcceptFailed(endpoint, ZError.EINVAL); return; } // Choose I/O thread to run connecter in. Given that we are already // running in an I/O thread, there must be at least one available. IOThread ioThread = chooseIoThread(options.affinity); assert (ioThread != null); // Create and launch a session object. SessionBase session = Sockets.createSession(ioThread, false, socket, options, null); assert (session != null); session.incSeqnum(); launchChild(session); sendAttach(session, engine, false); socket.eventAccepted(endpoint, channel); } // Close the listening socket. private void close() { assert (fd != null); try { fd.close(); socket.eventClosed(endpoint, fd); } catch (IOException e) { socket.eventCloseFailed(endpoint, ZError.exccode(e)); } fd = null; } public String getAddress() { return address(address); } protected String address(IZAddress address) { int port = fd.socket().getLocalPort(); return address.toString(port); } // Set address to listen on. public boolean setAddress(final String addr) { // Convert the textual address into address structure. address = new TcpAddress(addr, options.ipv6); return setAddress(); } // Set address to listen on, used by IpcListener that already resolved the address. protected boolean setAddress(InetSocketAddress addr) { // Convert the textual address into address structure. address = new TcpAddress(addr); return setAddress(); } private boolean setAddress() { endpoint = address.toString(); // Create a listening socket. try { if (options.selectorChooser == null) { fd = ServerSocketChannel.open(); } else { fd = options.selectorChooser.choose(address, options).openServerSocketChannel(); } // On some systems, IPv4 mapping in IPv6 sockets is disabled by default. // Switch it on in such cases. // The method enableIpv4Mapping is empty. Still to be written if (address.family() == StandardProtocolFamily.INET6) { TcpUtils.enableIpv4Mapping(fd); } TcpUtils.unblockSocket(fd); // Set the socket buffer limits for the underlying socket. if (options.sndbuf != 0) { TcpUtils.setTcpSendBuffer(fd, options.sndbuf); } if (options.rcvbuf != 0) { TcpUtils.setTcpReceiveBuffer(fd, options.rcvbuf); } if (!isWindows) { TcpUtils.setReuseAddress(fd, true); } // Bind the socket to the network interface and port. // NB: fd.socket().bind(...) for Android environments fd.socket().bind(address.address(), options.backlog); // find the address in case of wildcard endpoint = getAddress(); } catch (IOException e) { close(); errno.set(ZError.EADDRINUSE); return false; } socket.eventListening(endpoint, fd); return true; } // Accept the new connection. Returns the file descriptor of the // newly created connection. The function may throw IOException // if the connection was dropped while waiting in the listen backlog // or was denied because of accept filters. private SocketChannel accept() throws IOException { // The situation where connection cannot be accepted due to insufficient // resources is considered valid and treated by ignoring the connection. // Accept one connection and deal with different failure modes. assert (fd != null); SocketChannel sock = fd.accept(); if (!options.tcpAcceptFilters.isEmpty()) { boolean matched = false; for (TcpAddress.TcpAddressMask am : options.tcpAcceptFilters) { if (am.matchAddress(address.address())) { matched = true; break; } } if (!matched) { try { sock.close(); } catch (IOException ignored) { // Ignored } return null; } } if (options.tos != 0) { TcpUtils.setIpTypeOfService(sock, options.tos); } // Set the socket buffer limits for the underlying socket. if (options.sndbuf != 0) { TcpUtils.setTcpSendBuffer(sock, options.sndbuf); } if (options.rcvbuf != 0) { TcpUtils.setTcpReceiveBuffer(sock, options.rcvbuf); } if (!isWindows) { TcpUtils.setReuseAddress(sock, true); } return sock; } @Override public String toString() { return getClass().getSimpleName() + "[" + options.socketId + "]"; } } jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpUtils.java000066400000000000000000000125161455771126300226720ustar00rootroot00000000000000package zmq.io.net.tcp; import java.io.IOException; import java.net.SocketOption; import java.net.StandardSocketOptions; import java.nio.channels.Channel; import java.nio.channels.NetworkChannel; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import zmq.ZError; import zmq.io.net.Address; import zmq.util.Utils; public class TcpUtils { public static final boolean WITH_EXTENDED_KEEPALIVE = SocketOptionsProvider.WITH_EXTENDED_KEEPALIVE; @SuppressWarnings("unchecked") private static final class SocketOptionsProvider { // Wrapped in an inner class, to avoid the @SuppressWarnings for the whole class private static final SocketOption TCP_KEEPCOUNT; private static final SocketOption TCP_KEEPIDLE; private static final SocketOption TCP_KEEPINTERVAL; private static final boolean WITH_EXTENDED_KEEPALIVE; static { SocketOption tryCount = null; SocketOption tryIdle = null; SocketOption tryInterval = null; boolean extendedKeepAlive = false; try { Class eso = TcpUtils.class.getClassLoader().loadClass("jdk.net.ExtendedSocketOptions"); tryCount = (SocketOption) eso.getField("TCP_KEEPCOUNT").get(null); tryIdle = (SocketOption) eso.getField("TCP_KEEPIDLE").get(null); tryInterval = (SocketOption) eso.getField("TCP_KEEPINTERVAL").get(null); extendedKeepAlive = true; } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { // If failing, will keep extendedKeepAlive to false } TCP_KEEPCOUNT = tryCount; TCP_KEEPIDLE = tryIdle; TCP_KEEPINTERVAL = tryInterval; WITH_EXTENDED_KEEPALIVE = extendedKeepAlive; } } private TcpUtils() { } // The explicit IOException is useless, but kept for API compatibility public static void tuneTcpSocket(Channel channel) throws IOException { // Disable Nagle's algorithm. We are doing data batching on 0MQ level, // so using Nagle wouldn't improve throughput in any way, but it would // hurt latency. setOption(channel, StandardSocketOptions.TCP_NODELAY, true); } public static void tuneTcpKeepalives(Channel channel, int tcpKeepAlive, int tcpKeepAliveCnt, int tcpKeepAliveIdle, int tcpKeepAliveIntvl) { if (tcpKeepAlive != -1) { if (channel instanceof SocketChannel) { setOption(channel, StandardSocketOptions.SO_KEEPALIVE, tcpKeepAlive == 1); } if (WITH_EXTENDED_KEEPALIVE && tcpKeepAlive == 1) { if (tcpKeepAliveCnt > 0) { setOption(channel, SocketOptionsProvider.TCP_KEEPCOUNT, tcpKeepAliveCnt); } if (tcpKeepAliveIdle > 0) { setOption(channel, SocketOptionsProvider.TCP_KEEPIDLE, tcpKeepAliveIdle); } if (tcpKeepAliveIntvl > 0) { setOption(channel, SocketOptionsProvider.TCP_KEEPINTERVAL, tcpKeepAliveIntvl); } } } } public static boolean setTcpReceiveBuffer(Channel channel, final int rcvbuf) { setOption(channel, StandardSocketOptions.SO_RCVBUF, rcvbuf); return true; } public static boolean setTcpSendBuffer(Channel channel, final int sndbuf) { setOption(channel, StandardSocketOptions.SO_SNDBUF, sndbuf); return true; } public static boolean setIpTypeOfService(Channel channel, final int tos) { setOption(channel, StandardSocketOptions.SO_SNDBUF, tos); return true; } public static boolean setReuseAddress(Channel channel, final boolean reuse) { setOption(channel, StandardSocketOptions.SO_REUSEADDR, reuse); return true; } private static void setOption(Channel channel, SocketOption option, T value) { try { if (channel instanceof NetworkChannel) { ((NetworkChannel) channel).setOption(option, value); } else { throw new IllegalArgumentException("Channel " + channel + " is not a network channel"); } } catch (IOException e) { throw new ZError.IOException(e); } } public static void unblockSocket(SelectableChannel... channels) throws IOException { for (SelectableChannel ch : channels) { ch.configureBlocking(false); } } public static void enableIpv4Mapping(SelectableChannel channel) { // TODO V4 enable ipv4 mapping } /** * Return the {@link Address} of the channel * @param channel the channel, should be a TCP socket channel * @return The {@link Address} of the channel * @deprecated Use {@link zmq.util.Utils#getPeerIpAddress(SocketChannel)} instead * @throws ZError.IOException if the channel is closed or an I/O errors occurred * @throws IllegalArgumentException if the SocketChannel is not a TCP channel */ @Deprecated public static Address getPeerIpAddress(SocketChannel channel) { return Utils.getPeerIpAddress(channel); } } jeromq-0.6.0/src/main/java/zmq/io/net/tipc/000077500000000000000000000000001455771126300204245ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/io/net/tipc/TipcConnecter.java000066400000000000000000000010101455771126300240170ustar00rootroot00000000000000package zmq.io.net.tipc; import zmq.Options; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.io.net.tcp.TcpConnecter; public class TipcConnecter extends TcpConnecter { public TipcConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr, boolean wait) { super(ioThread, session, options, addr, wait); // TODO V4 implement Tipc throw new UnsupportedOperationException("TODO implement Tipc"); } } jeromq-0.6.0/src/main/java/zmq/io/net/tipc/TipcListener.java000066400000000000000000000006501455771126300236750ustar00rootroot00000000000000package zmq.io.net.tipc; import zmq.Options; import zmq.SocketBase; import zmq.io.IOThread; import zmq.io.net.tcp.TcpListener; public class TipcListener extends TcpListener { public TipcListener(IOThread ioThread, SocketBase socket, final Options options) { super(ioThread, socket, options); // TODO V4 implement tipc throw new UnsupportedOperationException("TODO implement tipc"); } } jeromq-0.6.0/src/main/java/zmq/msg/000077500000000000000000000000001455771126300170565ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocator.java000066400000000000000000000001411455771126300223040ustar00rootroot00000000000000package zmq.msg; import zmq.Msg; public interface MsgAllocator { Msg allocate(int size); } jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorDirect.java000066400000000000000000000003611455771126300234430ustar00rootroot00000000000000package zmq.msg; import java.nio.ByteBuffer; import zmq.Msg; public class MsgAllocatorDirect implements MsgAllocator { @Override public Msg allocate(int size) { return new Msg(ByteBuffer.allocateDirect(size)); } } jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorHeap.java000066400000000000000000000002671455771126300231130ustar00rootroot00000000000000package zmq.msg; import zmq.Msg; public class MsgAllocatorHeap implements MsgAllocator { @Override public Msg allocate(int size) { return new Msg(size); } } jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorThreshold.java000066400000000000000000000013311455771126300241630ustar00rootroot00000000000000package zmq.msg; import zmq.Config; import zmq.Msg; public class MsgAllocatorThreshold implements MsgAllocator { private static final MsgAllocator direct = new MsgAllocatorDirect(); private static final MsgAllocator heap = new MsgAllocatorHeap(); public final int threshold; public MsgAllocatorThreshold() { this(Config.MSG_ALLOCATION_HEAP_THRESHOLD.getValue()); } public MsgAllocatorThreshold(int threshold) { this.threshold = threshold; } @Override public Msg allocate(int size) { if (threshold > 0 && size > threshold) { return direct.allocate(size); } else { return heap.allocate(size); } } } jeromq-0.6.0/src/main/java/zmq/msg/package-info.java000066400000000000000000000005521455771126300222470ustar00rootroot00000000000000/** * Provides utility for message allocation within ØMQ. *
* This is a java-only construct, allowing to customize the creation of messages (potentially sharing buffers, for instance). * *

The classes of this package shall be used with {@link zmq.ZMQ#ZMQ_MSG_ALLOCATOR} or {@link zmq.ZMQ#ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD}

*/ package zmq.msg; jeromq-0.6.0/src/main/java/zmq/package-info.java000066400000000000000000000014451455771126300214630ustar00rootroot00000000000000/** *

Provides low-level bindings for ØMQ.

* *

This is the java equivalent of libzmq project.

* *

The ZeroMQ lightweight messaging kernel is a library which extends the standard socket interfaces * with features traditionally provided by specialised messaging middleware products. * ZeroMQ sockets provide an abstraction of asynchronous message queues, * multiple messaging patterns, message filtering (subscriptions), * seamless access to multiple transport protocols and more.

* *

All subpackages should be considered internal, with the exception of {@link zmq.msg}.

* *

Within this package, only {@link zmq.ZMQ}, {@link zmq.Ctx}, {@link zmq.SocketBase} and {@link zmq.Msg} should be used.

*/ package zmq; jeromq-0.6.0/src/main/java/zmq/pipe/000077500000000000000000000000001455771126300172255ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/pipe/DBuffer.java000066400000000000000000000024241455771126300214070ustar00rootroot00000000000000package zmq.pipe; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import zmq.Msg; class DBuffer { private T back; private T front; private final Lock sync = new ReentrantLock(); private boolean hasMsg; public T back() { return back; } public T front() { return front; } void write(T msg) { assert (msg.check()); sync.lock(); try { back = front; front = msg; hasMsg = true; } finally { sync.unlock(); } } T read() { sync.lock(); try { if (!hasMsg) { return null; } assert (front.check()); // TODO front->init (); // avoid double free hasMsg = false; return front; } finally { sync.unlock(); } } boolean checkRead() { sync.lock(); try { return hasMsg; } finally { sync.unlock(); } } T probe() { sync.lock(); try { return front; } finally { sync.unlock(); } } } jeromq-0.6.0/src/main/java/zmq/pipe/Pipe.java000066400000000000000000000464371455771126300210030ustar00rootroot00000000000000package zmq.pipe; import zmq.Config; import zmq.Msg; import zmq.ZObject; import zmq.util.Blob; // Note that pipe can be stored in three different arrays. // The array of inbound pipes (1), the array of outbound pipes (2) and // the generic array of pipes to deallocate (3). public class Pipe extends ZObject { public interface IPipeEvents { void readActivated(Pipe pipe); void writeActivated(Pipe pipe); void hiccuped(Pipe pipe); void pipeTerminated(Pipe pipe); } // Underlying pipes for both directions. private YPipeBase inpipe; private YPipeBase outpipe; // Can the pipe be read from / written to? private boolean inActive; private boolean outActive; // High watermark for the outbound pipe. private int hwm; // Low watermark for the inbound pipe. private int lwm; // Number of messages read and written so far. private long msgsRead; private long msgsWritten; // Last received peer's msgsRead. The actual number in the peer // can be higher at the moment. private long peersMsgsRead; // The pipe object on the other side of the pipepair. private Pipe peer; // Sink to send events to. private IPipeEvents sink; // States of the pipe endpoint: // active: common state before any termination begins, // delimiter_received: delimiter was read from pipe before // term command was received, // waiting_fo_delimiter: term command was already received // from the peer but there are still pending messages to read, // term_ack_sent: all pending messages were already read and // all we are waiting for is ack from the peer, // term_req_sent1: 'terminate' was explicitly called by the user, // term_req_sent2: user called 'terminate' and then we've got // term command from the peer as well. enum State { ACTIVE, DELIMITER_RECEIVED, WAITING_FOR_DELIMITER, TERM_ACK_SENT, TERM_REQ_SENT_1, TERM_REQ_SENT_2 } private State state; // If true, we receive all the pending inbound messages before // terminating. If false, we terminate immediately when the peer // asks us to. private boolean delay; // Identity of the writer. Used uniquely by the reader side. private Blob identity; // Routing id of the writer. Used uniquely by the reader side. private int routingId; // Pipe's credential. private Blob credential; private final boolean conflate; private Msg disconnectMsg; // JeroMQ only private final ZObject parent; // Constructor is private. Pipe can only be created using // pipepair function. private Pipe(ZObject parent, YPipeBase inpipe, YPipeBase outpipe, int inhwm, int outhwm, boolean conflate) { super(parent); this.inpipe = inpipe; this.outpipe = outpipe; inActive = true; outActive = true; hwm = outhwm; lwm = computeLwm(inhwm); msgsRead = 0; msgsWritten = 0; peersMsgsRead = 0; peer = null; sink = null; state = State.ACTIVE; this.delay = true; this.conflate = conflate; disconnectMsg = null; this.parent = parent; } // This allows pipepair to create pipe objects. // Create a pipepair for bi-directional transfer of messages. // First HWM is for messages passed from first pipe to the second pipe. // Second HWM is for messages passed from second pipe to the first pipe. // Conflate specifies how the pipe behaves when the peer terminates. If true // pipe receives all the pending messages before terminating, otherwise it // terminates straight away. public static Pipe[] pair(ZObject[] parents, int[] hwms, boolean[] conflates) { Pipe[] pipes = new Pipe[2]; // Creates two pipe objects. These objects are connected by two ypipes, // each to pass messages in one direction. YPipeBase upipe1 = conflates[0] ? new YPipeConflate<>() : new YPipe<>(Config.MESSAGE_PIPE_GRANULARITY.getValue()); YPipeBase upipe2 = conflates[1] ? new YPipeConflate<>() : new YPipe<>(Config.MESSAGE_PIPE_GRANULARITY.getValue()); pipes[0] = new Pipe(parents[0], upipe1, upipe2, hwms[1], hwms[0], conflates[0]); pipes[1] = new Pipe(parents[1], upipe2, upipe1, hwms[0], hwms[1], conflates[1]); pipes[0].setPeer(pipes[1]); pipes[1].setPeer(pipes[0]); return pipes; } // Pipepair uses this function to let us know about // the peer pipe object. private void setPeer(Pipe peer) { // Peer can be set once only. assert (this.peer == null); assert (peer != null); this.peer = peer; } // Specifies the object to send events to. public void setEventSink(IPipeEvents sink) { assert (this.sink == null); this.sink = sink; } // Pipe endpoint can store an opaque ID to be used by its clients. public void setIdentity(Blob identity) { this.identity = identity; } public Blob getIdentity() { return identity; } public void setRoutingId(int routingId) { this.routingId = routingId; } public int getRoutingId() { return routingId; } public Blob getCredential() { return credential; } // Returns true if there is at least one message to read in the pipe. public boolean checkRead() { if (!inActive) { return false; } if (state != State.ACTIVE && state != State.WAITING_FOR_DELIMITER) { return false; } // Check if there's an item in the pipe. if (!inpipe.checkRead()) { inActive = false; return false; } // If the next item in the pipe is message delimiter, // initiate termination process. if (isDelimiter(inpipe.probe())) { Msg msg = inpipe.read(); assert (msg != null); processDelimiter(); return false; } return true; } // Reads a message to the underlying pipe. public Msg read() { if (!inActive) { return null; } if (state != State.ACTIVE && state != State.WAITING_FOR_DELIMITER) { return null; } while (true) { Msg msg = inpipe.read(); if (msg == null) { inActive = false; return null; } // If this is a credential, save a copy and receive next message. if (msg.isCredential()) { credential = Blob.createBlob(msg); continue; } // If delimiter was read, start termination process of the pipe. if (msg.isDelimiter()) { processDelimiter(); return null; } if (!msg.hasMore() && !msg.isIdentity()) { msgsRead++; } if (lwm > 0 && msgsRead % lwm == 0) { sendActivateWrite(peer, msgsRead); } return msg; } } // Checks whether messages can be written to the pipe. If writing // the message would cause high watermark the function returns false. public boolean checkWrite() { if (!outActive || state != State.ACTIVE) { return false; } // TODO DIFF V4 small change, it is done like this in 4.2.2 boolean full = !checkHwm(); if (full) { outActive = false; return false; } return true; } // Writes a message to the underlying pipe. Returns false if the // message cannot be written because high watermark was reached. public boolean write(Msg msg) { if (!checkWrite()) { return false; } boolean more = msg.hasMore(); boolean identity = msg.isIdentity(); outpipe.write(msg, more); if (!more && !identity) { msgsWritten++; } return true; } // Remove unfinished parts of the outbound message from the pipe. public void rollback() { // Remove incomplete message from the outbound pipe. Msg msg; if (outpipe != null) { while ((msg = outpipe.unwrite()) != null) { assert (msg.hasMore()); } } } // Flush the messages downstream. public void flush() { // The peer does not exist anymore at this point. if (state == State.TERM_ACK_SENT) { return; } if (outpipe != null && !outpipe.flush()) { sendActivateRead(peer); } } @Override protected void processActivateRead() { if (!inActive && (state == State.ACTIVE || state == State.WAITING_FOR_DELIMITER)) { inActive = true; sink.readActivated(this); } } @Override protected void processActivateWrite(long msgsRead) { // Remember the peers's message sequence number. peersMsgsRead = msgsRead; if (!outActive && state == State.ACTIVE) { outActive = true; sink.writeActivated(this); } } @Override protected void processHiccup(YPipeBase pipe) { // Destroy old outpipe. Note that the read end of the pipe was already // migrated to this thread. assert (outpipe != null); outpipe.flush(); Msg msg; while ((msg = outpipe.read()) != null) { if (!msg.hasMore()) { msgsWritten--; } } // Plug in the new outpipe. assert (pipe != null); outpipe = pipe; outActive = true; // If appropriate, notify the user about the hiccup. if (state == State.ACTIVE) { sink.hiccuped(this); } } @Override protected void processPipeTerm() { assert (state == State.ACTIVE || state == State.DELIMITER_RECEIVED || state == State.TERM_REQ_SENT_1); // This is the simple case of peer-induced termination. If there are no // more pending messages to read, or if the pipe was configured to drop // pending messages, we can move directly to the term_ack_sent state. // Otherwise we'll hang up in waiting_for_delimiter state till all // pending messages are read. if (state == State.ACTIVE) { if (delay) { state = State.WAITING_FOR_DELIMITER; } else { state = State.TERM_ACK_SENT; outpipe = null; sendPipeTermAck(peer); } } else // Delimiter happened to arrive before the term command. Now we have the // term command as well, so we can move straight to term_ack_sent state. if (state == State.DELIMITER_RECEIVED) { state = State.TERM_ACK_SENT; outpipe = null; sendPipeTermAck(peer); } else // This is the case where both ends of the pipe are closed in parallel. // We simply reply to the request by ack and continue waiting for our // own ack. if (state == State.TERM_REQ_SENT_1) { state = State.TERM_REQ_SENT_2; outpipe = null; sendPipeTermAck(peer); } } @Override protected void processPipeTermAck() { // Notify the user that all the references to the pipe should be dropped. assert (sink != null); sink.pipeTerminated(this); // In term_ack_sent and term_req_sent2 states there's nothing to do. // Simply deallocate the pipe. In term_req_sent1 state we have to ack // the peer before deallocating this side of the pipe. // All the other states are invalid. if (state == State.TERM_REQ_SENT_1) { outpipe = null; sendPipeTermAck(peer); } else { assert (state == State.TERM_ACK_SENT || state == State.TERM_REQ_SENT_2); } // TODO V4 not in zeromq, but no harm. Remove it? // If the inbound pipe has already been deallocated, then we're done. if (inpipe == null) { return; } // We'll deallocate the inbound pipe, the peer will deallocate the outbound // pipe (which is an inbound pipe from its point of view). // First, delete all the unread messages in the pipe. We have to do it by // hand because msg_t doesn't have automatic destructor. Then deallocate // the ypipe itself. if (!conflate) { while (inpipe.read() != null) { // do nothing } } // Deallocate the pipe object inpipe = null; } public void setNoDelay() { this.delay = false; } // Ask pipe to terminate. The termination will happen asynchronously // and user will be notified about actual deallocation by 'terminated' // event. If delay is true, the pending messages will be processed // before actual shutdown. public void terminate(boolean delay) { // Overload the value specified at pipe creation. this.delay = delay; // If terminate was already called, we can ignore the duplicit invocation. if (state == State.TERM_REQ_SENT_1 || state == State.TERM_REQ_SENT_2) { return; } // If the pipe is in the final phase of async termination, it's going to // closed anyway. No need to do anything special here. else if (state == State.TERM_ACK_SENT) { return; } // The simple sync termination case. Ask the peer to terminate and wait // for the ack. else if (state == State.ACTIVE) { sendPipeTerm(peer); state = State.TERM_REQ_SENT_1; } // There are still pending messages available, but the user calls // 'terminate'. We can act as if all the pending messages were read. else if (state == State.WAITING_FOR_DELIMITER && !this.delay) { outpipe = null; sendPipeTermAck(peer); state = State.TERM_ACK_SENT; } // If there are pending messages still available, do nothing. else if (state == State.WAITING_FOR_DELIMITER) { // do nothing } // We've already got delimiter, but not term command yet. We can ignore // the delimiter and ack synchronously terminate as if we were in // active state. else if (state == State.DELIMITER_RECEIVED) { sendPipeTerm(peer); state = State.TERM_REQ_SENT_1; } // There are no other states. else { assert (false); } // Stop outbound flow of messages. outActive = false; if (outpipe != null) { // Drop any unfinished outbound messages. rollback(); // Write the delimiter into the pipe. Note that watermarks are not // checked; thus the delimiter can be written even when the pipe is full. Msg msg = new Msg(); msg.initDelimiter(); outpipe.write(msg, false); flush(); } } // Returns true if the message is delimiter; false otherwise. private static boolean isDelimiter(Msg msg) { return msg.isDelimiter(); } // Computes appropriate low watermark from the given high watermark. private static int computeLwm(int hwm) { // Compute the low water mark. Following point should be taken // into consideration: // // 1. LWM has to be less than HWM. // 2. LWM cannot be set to very low value (such as zero) as after filling // the queue it would start to refill only after all the messages are // read from it and thus unnecessarily hold the progress back. // 3. LWM cannot be set to very high value (such as HWM-1) as it would // result in lock-step filling of the queue - if a single message is // read from a full queue, writer thread is resumed to write exactly one // message to the queue and go back to sleep immediately. This would // result in low performance. // // Given the 3. it would be good to keep HWM and LWM as far apart as // possible to reduce the thread switching overhead to almost zero, // say HWM-LWM should be max_wm_delta. // // That done, we still we have to account for the cases where // HWM < max_wm_delta thus driving LWM to negative numbers. // Let's make LWM 1/2 of HWM in such cases. // return (hwm +1) /2; return (hwm + 1) / 2; } // Handler for delimiter read from the pipe. private void processDelimiter() { assert (state == State.ACTIVE || state == State.WAITING_FOR_DELIMITER); if (state == State.ACTIVE) { state = State.DELIMITER_RECEIVED; } else { outpipe = null; sendPipeTermAck(peer); state = State.TERM_ACK_SENT; } } // Temporarily disconnects the inbound message stream and drops // all the messages on the fly. Causes 'hiccuped' event to be generated // in the peer. public void hiccup() { // If termination is already under way do nothing. if (state != State.ACTIVE) { return; } // We'll drop the pointer to the inpipe. From now on, the peer is // responsible for deallocating it. inpipe = null; // Create new inpipe. if (conflate) { inpipe = new YPipeConflate<>(); } else { inpipe = new YPipe<>(Config.MESSAGE_PIPE_GRANULARITY.getValue()); } inActive = true; // Notify the peer about the hiccup. sendHiccup(peer, inpipe); } public void setHwms(int inhwm, int outhwm) { lwm = computeLwm(inhwm); hwm = outhwm; } public boolean checkHwm() { // TODO DIFF V4 small change, it is done like this in 4.2.2 boolean full = hwm > 0 && (msgsWritten - peersMsgsRead) >= hwm; return !full; } public void setDisconnectMsg(Msg msg) { disconnectMsg = msg; } public void sendDisconnectMsg() { if (disconnectMsg != null && outpipe != null) { // Rollback any incomplete message in the pipe, and push the disconnect message. rollback(); outpipe.write(disconnectMsg, false); flush(); disconnectMsg = null; } } public void sendHiccupMsg(Msg hiccupMsg) { if (hiccupMsg != null && outpipe != null) { // Rollback any incomplete message in the pipe, and push the hiccupMsg message. rollback(); outpipe.write(hiccupMsg, false); flush(); } } @Override public String toString() { return super.toString() + "(" + parent.getClass().getSimpleName() + "[" + parent.getTid() + "]->" + peer.parent.getClass().getSimpleName() + "[" + peer.parent.getTid() + "])"; } } jeromq-0.6.0/src/main/java/zmq/pipe/YPipe.java000066400000000000000000000111231455771126300211140ustar00rootroot00000000000000package zmq.pipe; import java.util.concurrent.atomic.AtomicInteger; public class YPipe implements YPipeBase { // Allocation-efficient queue to store pipe items. // Front of the queue points to the first prefetched item, back of // the pipe points to last un-flushed item. Front is used only by // reader thread, while back is used only by writer thread. private final YQueue queue; // Points to the first un-flushed item. This variable is used // exclusively by writer thread. private int w; // Points to the first un-prefetched item. This variable is used // exclusively by reader thread. private int r; // Points to the first item to be flushed in the future. private int f; // The single point of contention between writer and reader thread. // Points past the last flushed item. If it is NULL, // reader is asleep. This pointer should be always accessed using // atomic operations. private final AtomicInteger c; public YPipe(int qsize) { queue = new YQueue<>(qsize); int pos = queue.backPos(); f = pos; r = pos; w = pos; c = new AtomicInteger(pos); } // Write an item to the pipe. Don't flush it yet. If incomplete is // set to true the item is assumed to be continued by items // subsequently written to the pipe. Incomplete items are never // flushed down the stream. @Override public void write(final T value, boolean incomplete) { // Place the value to the queue, add new terminator element. queue.push(value); // Move the "flush up to here" pointer. if (!incomplete) { f = queue.backPos(); } } // Pop an incomplete item from the pipe. Returns true is such // item exists, false otherwise. @Override public T unwrite() { if (f == queue.backPos()) { return null; } queue.unpush(); return queue.back(); } // Flush all the completed items into the pipe. Returns false if // the reader thread is sleeping. In that case, caller is obliged to // wake the reader up before using the pipe again. @Override public boolean flush() { // If there are no un-flushed items, do nothing. if (w == f) { return true; } // Try to set 'c' to 'f'. if (!c.compareAndSet(w, f)) { // Compare-and-swap was unsuccessful because 'c' is NULL. // This means that the reader is asleep. Therefore we don't // care about thread-safeness and update c in non-atomic // manner. We'll return false to let the caller know // that reader is sleeping. c.set(f); w = f; return false; } // Reader is alive. Nothing special to do now. Just move // the 'first un-flushed item' pointer to 'f'. w = f; return true; } // Check whether item is available for reading. @Override public boolean checkRead() { // Was the value prefetched already? If so, return. int h = queue.frontPos(); if (h != r && r != -1) { return true; } // There's no prefetched value, so let us prefetch more values. // Prefetching is to simply retrieve the // pointer from c in atomic fashion. If there are no // items to prefetch, set c to -1 (using compare-and-swap). if (c.compareAndSet(h, -1)) { // nothing to read, h == r must be the same } else { // something to have been written r = c.get(); } // If there are no elements prefetched, exit. // During pipe's lifetime r should never be NULL, however, // it can happen during pipe shutdown when items // are being deallocated. return h != r && r != -1; // There was at least one value prefetched. } // Reads an item from the pipe. Returns null if there is no value. // available. @Override public T read() { // Try to prefetch a value. if (!checkRead()) { return null; } // There was at least one value prefetched. // Return it to the caller. return queue.pop(); } // Returns the first element in the pipe without removing it. // The pipe mustn't be empty or the function crashes. @Override public T probe() { boolean rc = checkRead(); assert (rc); return queue.front(); } } jeromq-0.6.0/src/main/java/zmq/pipe/YPipeBase.java000066400000000000000000000020531455771126300217110ustar00rootroot00000000000000package zmq.pipe; public interface YPipeBase { // Write an item to the pipe. Don't flush it yet. If incomplete is // set to true the item is assumed to be continued by items // subsequently written to the pipe. Incomplete items are never // flushed down the stream. void write(final T value, boolean incomplete); // Pop an incomplete item from the pipe. Returns true is such // item exists, false otherwise. T unwrite(); // Flush all the completed items into the pipe. Returns false if // the reader thread is sleeping. In that case, caller is obliged to // wake the reader up before using the pipe again. boolean flush(); // Check whether item is available for reading. boolean checkRead(); // Reads an item from the pipe. Returns false if there is no value. // available. T read(); // Applies the function fn to the first elemenent in the pipe // and returns the value returned by the fn. // The pipe mustn't be empty or the function crashes. T probe(); } jeromq-0.6.0/src/main/java/zmq/pipe/YPipeConflate.java000066400000000000000000000042001455771126300225660ustar00rootroot00000000000000package zmq.pipe; import zmq.Msg; // Adapter for dbuffer, to plug it in instead of a queue for the sake // of implementing the conflate socket option, which, if set, makes // the receiving side to discard all incoming messages but the last one. // // reader_awake flag is needed here to mimic ypipe delicate behaviour // around the reader being asleep (see 'c' pointer being NULL in ypipe.hpp) public class YPipeConflate implements YPipeBase { private boolean readerAwake; private final DBuffer dbuffer = new DBuffer<>(); // Following function (write) deliberately copies uninitialised data // when used with zmq_msg. Initialising the VSM body for // non-VSM messages won't be good for performance. @Override public void write(final T value, boolean incomplete) { dbuffer.write(value); } // There are no incomplete items for conflate ypipe @Override public T unwrite() { return null; } // Flush is no-op for conflate ypipe. Reader asleep behaviour // is as of the usual ypipe. // Returns false if the reader thread is sleeping. In that case, // caller is obliged to wake the reader up before using the pipe again. @Override public boolean flush() { return readerAwake; } // Check whether item is available for reading. @Override public boolean checkRead() { boolean rc = dbuffer.checkRead(); if (!rc) { readerAwake = false; } return rc; } // Reads an item from the pipe. Returns false if there is no value. // available. @Override public T read() { // Try to prefetch a value. if (!checkRead()) { return null; } // There was at least one value prefetched. // Return it to the caller. return dbuffer.read(); } // Applies the function fn to the first elemenent in the pipe // and returns the value returned by the fn. // The pipe mustn't be empty or the function crashes. @Override public T probe() { return dbuffer.probe(); } } jeromq-0.6.0/src/main/java/zmq/pipe/YQueue.java000066400000000000000000000104711455771126300213100ustar00rootroot00000000000000package zmq.pipe; class YQueue { // Individual memory chunk to hold N elements. private static class Chunk { final T[] values; final int[] pos; Chunk prev; Chunk next; @SuppressWarnings("unchecked") public Chunk(int size, int memoryPtr) { values = (T[]) new Object[size]; pos = new int[size]; for (int i = 0; i != values.length; i++) { pos[i] = memoryPtr; memoryPtr++; } } } // Back position may point to invalid memory if the queue is empty, // while begin & end positions are always valid. Begin position is // accessed exclusively be queue reader (front/pop), while back and // end positions are accessed exclusively by queue writer (back/push). private Chunk beginChunk; private int beginPos; private Chunk backChunk; private int backPos; private Chunk endChunk; private int endPos; private volatile Chunk spareChunk; private final int size; // People are likely to produce and consume at similar rates. In // this scenario holding onto the most recently freed chunk saves // us from having to call malloc/free. private int memoryPtr; public YQueue(int size) { this.size = size; memoryPtr = 0; beginChunk = new Chunk<>(size, memoryPtr); memoryPtr += size; beginPos = 0; backPos = 0; backChunk = beginChunk; spareChunk = beginChunk; endChunk = beginChunk; endPos = 1; } public int frontPos() { return beginChunk.pos[beginPos]; } // Returns reference to the front element of the queue. // If the queue is empty, behaviour is undefined. public T front() { return beginChunk.values[beginPos]; } public int backPos() { return backChunk.pos[backPos]; } // Returns reference to the back element of the queue. // If the queue is empty, behaviour is undefined. public T back() { return backChunk.values[backPos]; } // Adds an element to the back end of the queue. public void push(T val) { backChunk.values[backPos] = val; backChunk = endChunk; backPos = endPos; if (++endPos != size) { return; } Chunk sc = spareChunk; if (sc != beginChunk) { spareChunk = spareChunk.next; endChunk.next = sc; sc.prev = endChunk; } else { endChunk.next = new Chunk<>(size, memoryPtr); memoryPtr += size; endChunk.next.prev = endChunk; } endChunk = endChunk.next; endPos = 0; } // Removes element from the back end of the queue. In other words // it rollbacks last push to the queue. Take care: Caller is // responsible for destroying the object being unpushed. // The caller must also guarantee that the queue isn't empty when // unpush is called. It cannot be done automatically as the read // side of the queue can be managed by different, completely // unsynchronised thread. public void unpush() { // First, move 'back' one position backwards. if (backPos > 0) { --backPos; } else { backPos = size - 1; backChunk = backChunk.prev; } // Now, move 'end' position backwards. Note that obsolete end chunk // is not used as a spare chunk. The analysis shows that doing so // would require free and atomic operation per chunk deallocated // instead of a simple free. if (endPos > 0) { --endPos; } else { endPos = size - 1; endChunk = endChunk.prev; endChunk.next = null; } } // Removes an element from the front end of the queue. public T pop() { T val = beginChunk.values[beginPos]; beginChunk.values[beginPos] = null; beginPos++; if (beginPos == size) { beginChunk = beginChunk.next; beginChunk.prev = null; beginPos = 0; } return val; } } jeromq-0.6.0/src/main/java/zmq/poll/000077500000000000000000000000001455771126300172365ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/poll/IPollEvents.java000066400000000000000000000017301455771126300223060ustar00rootroot00000000000000package zmq.poll; public interface IPollEvents { /** * Called by I/O thread when file descriptor is ready for reading. */ default void inEvent() { throw new UnsupportedOperationException(); } /** * Called by I/O thread when file descriptor is ready for writing. */ default void outEvent() { throw new UnsupportedOperationException(); } /** * Called by I/O thread when file descriptor might be ready for connecting. */ default void connectEvent() { throw new UnsupportedOperationException(); } /** * Called by I/O thread when file descriptor is ready for accept. */ default void acceptEvent() { throw new UnsupportedOperationException(); } /** * Called when timer expires. * * @param id the ID of the expired timer. */ default void timerEvent(int id) { throw new UnsupportedOperationException(); } } jeromq-0.6.0/src/main/java/zmq/poll/PollItem.java000066400000000000000000000072731455771126300216370ustar00rootroot00000000000000package zmq.poll; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import zmq.SocketBase; import zmq.ZMQ; public class PollItem { private final SocketBase socket; private final SelectableChannel channel; private final int zinterest; private final int interest; private int ready; public PollItem(SocketBase socket, int ops) { this(socket, null, ops); } public PollItem(SelectableChannel channel, int ops) { this(null, channel, ops); } private PollItem(SocketBase socket, SelectableChannel channel, int ops) { this.socket = socket; this.channel = channel; this.zinterest = ops; this.interest = init(ops); } private int init(int ops) { int interest = 0; if ((ops & ZMQ.ZMQ_POLLIN) > 0) { interest |= SelectionKey.OP_READ; } if ((ops & ZMQ.ZMQ_POLLOUT) > 0) { if (socket != null) { // ZMQ Socket get readiness from the mailbox interest |= SelectionKey.OP_READ; } else { interest |= SelectionKey.OP_WRITE; } } this.ready = 0; return interest; } public boolean isReadable() { return (ready & ZMQ.ZMQ_POLLIN) > 0; } public boolean isWritable() { return (ready & ZMQ.ZMQ_POLLOUT) > 0; } public boolean isError() { return (ready & ZMQ.ZMQ_POLLERR) > 0; } public SocketBase getSocket() { return socket; } public SelectableChannel getRawSocket() { return channel; } public SelectableChannel getChannel() { if (socket != null) { // If the poll item is a 0MQ socket we are interested in input on the // notification file descriptor retrieved by the ZMQ_FD socket option. return socket.getFD(); } else { // Else, the poll item is a raw file descriptor. Convert the poll item // events to the appropriate fd_sets. return channel; } } public int interestOps() { return interest; } public int zinterestOps() { return zinterest; } public boolean hasEvent(int events) { return (zinterest & events) > 0; } public int interestOps(int ops) { init(ops); return interest; } public int readyOps(SelectionKey key, int nevents) { ready = 0; if (socket != null) { // The poll item is a 0MQ socket. Retrieve pending events // using the ZMQ_EVENTS socket option. int events = socket.getSocketOpt(ZMQ.ZMQ_EVENTS); if (events < 0) { return -1; } if ((zinterest & ZMQ.ZMQ_POLLOUT) > 0 && (events & ZMQ.ZMQ_POLLOUT) > 0) { ready |= ZMQ.ZMQ_POLLOUT; } if ((zinterest & ZMQ.ZMQ_POLLIN) > 0 && (events & ZMQ.ZMQ_POLLIN) > 0) { ready |= ZMQ.ZMQ_POLLIN; } } // Else, the poll item is a raw file descriptor, simply convert // the events to zmq_pollitem_t-style format. else if (nevents > 0) { if (key.isReadable()) { ready |= ZMQ.ZMQ_POLLIN; } if (key.isWritable()) { ready |= ZMQ.ZMQ_POLLOUT; } if (!key.isValid() || key.isAcceptable() || key.isConnectable()) { ready |= ZMQ.ZMQ_POLLERR; } } return ready; } public int readyOps() { return ready; } } jeromq-0.6.0/src/main/java/zmq/poll/Poller.java000066400000000000000000000214711455771126300213430ustar00rootroot00000000000000package zmq.poll; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import zmq.Ctx; import zmq.ZError; public final class Poller extends PollerBase implements Runnable { // opaque class to mimic libzmq behaviour. // extra interest is we do not need to look through the fdTable to perform common operations. public static final class Handle { private final SelectableChannel fd; private final IPollEvents handler; private int ops; private boolean cancelled; public Handle(SelectableChannel fd, IPollEvents handler) { assert (fd != null); assert (handler != null); this.fd = fd; this.handler = handler; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + fd.hashCode(); result = prime * result + handler.hashCode(); return result; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (!(other instanceof Handle)) { return false; } Handle that = (Handle) other; return this.fd.equals(that.fd) && this.handler.equals(that.handler); } @Override public String toString() { return "Handle-" + fd; } } // Reference to ZMQ context. private final Ctx ctx; // stores data for registered descriptors. private final Set fdTable; // If true, there's at least one retired event source. private boolean retired = false; // If true, thread is in the process of shutting down. private final AtomicBoolean stopping = new AtomicBoolean(); private final CountDownLatch stopped = new CountDownLatch(1); private final UncaughtExceptionHandler exnotification; private Selector selector; public Poller(Ctx ctx, String name) { super(name, ctx.getThreadFactory()); this.ctx = ctx; exnotification = ctx.getNotificationExceptionHandler(); fdTable = new HashSet<>(); selector = ctx.createSelector(); } public void destroy() { try { stop(); stopped.await(); } catch (InterruptedException e) { e.printStackTrace(); // Re-interrupt the thread so the caller can handle it. Thread.currentThread().interrupt(); } finally { ctx.closeSelector(selector); } } public Handle addHandle(SelectableChannel fd, IPollEvents events) { assert (Thread.currentThread() == worker || !worker.isAlive()); Handle handle = new Handle(fd, events); fdTable.add(handle); // Increase the load metric of the thread. adjustLoad(1); return handle; } public void removeHandle(Handle handle) { assert (Thread.currentThread() == worker || !worker.isAlive()); // Mark the fd as unused. handle.cancelled = true; retired = true; // Decrease the load metric of the thread. adjustLoad(-1); } public void setPollIn(Handle handle) { register(handle, SelectionKey.OP_READ, true); } public void resetPollIn(Handle handle) { register(handle, SelectionKey.OP_READ, false); } public void setPollOut(Handle handle) { register(handle, SelectionKey.OP_WRITE, true); } public void resetPollOut(Handle handle) { register(handle, SelectionKey.OP_WRITE, false); } public void setPollConnect(Handle handle) { register(handle, SelectionKey.OP_CONNECT, true); } public void setPollAccept(Handle handle) { register(handle, SelectionKey.OP_ACCEPT, true); } private void register(Handle handle, int ops, boolean add) { assert (Thread.currentThread() == worker || !worker.isAlive()); if (add) { handle.ops |= ops; } else { handle.ops &= ~ops; } retired = true; } public void start() { worker.start(); } public void stop() { stopping.set(true); retired = false; selector.wakeup(); } @Override public void run() { int returnsImmediately = 0; while (!stopping.get()) { // Execute any due timers. long timeout = executeTimers(); if (retired) { retired = false; Iterator iter = fdTable.iterator(); while (iter.hasNext()) { Handle handle = iter.next(); SelectionKey key = handle.fd.keyFor(selector); if (handle.cancelled || !handle.fd.isOpen()) { if (key != null) { key.cancel(); } iter.remove(); continue; } if (key == null) { if (handle.fd.isOpen()) { try { key = handle.fd.register(selector, handle.ops, handle); assert (key != null); } catch (CancelledKeyException | ClosedSelectorException | ClosedChannelException e) { exnotification.uncaughtException(worker, e); } } } else if (key.isValid()) { key.interestOps(handle.ops); } } } // Wait for events. int rc; long start = System.currentTimeMillis(); try { rc = selector.select(timeout); } catch (ClosedSelectorException e) { rebuildSelector(); exnotification.uncaughtException(worker, e); ctx.errno().set(ZError.EINTR); continue; } catch (IOException e) { throw new ZError.IOException(e); } // If there are no events (i.e. it's a timeout) there's no point // in checking the keys. if (rc == 0) { returnsImmediately = maybeRebuildSelector(returnsImmediately, timeout, start); continue; } Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = it.next(); Handle pollset = (Handle) key.attachment(); it.remove(); if (pollset.cancelled) { continue; } try { if (key.isValid() && key.isAcceptable()) { pollset.handler.acceptEvent(); } if (key.isValid() && key.isConnectable()) { pollset.handler.connectEvent(); } if (key.isValid() && key.isWritable()) { pollset.handler.outEvent(); } if (key.isValid() && key.isReadable()) { pollset.handler.inEvent(); } } catch (RuntimeException e) { exnotification.uncaughtException(worker, e); } } } stopped.countDown(); } private int maybeRebuildSelector(int returnsImmediately, long timeout, long start) { // Guess JDK epoll bug if (timeout == 0 || System.currentTimeMillis() - start < timeout / 2) { returnsImmediately++; } else { returnsImmediately = 0; } if (returnsImmediately > 10) { rebuildSelector(); returnsImmediately = 0; } return returnsImmediately; } private void rebuildSelector() { Selector oldSelector = selector; selector = ctx.createSelector(); retired = true; ctx.closeSelector(oldSelector); } } jeromq-0.6.0/src/main/java/zmq/poll/PollerBase.java000066400000000000000000000124231455771126300221330ustar00rootroot00000000000000package zmq.poll; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import zmq.util.Clock; import zmq.util.MultiMap; import zmq.util.function.BiFunction; abstract class PollerBase implements Runnable { private static final class TimerInfo { private final IPollEvents sink; private final int id; private boolean cancelled; public TimerInfo(IPollEvents sink, int id) { assert (sink != null); this.sink = sink; this.id = id; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + sink.hashCode(); return result; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (!(other instanceof TimerInfo)) { return false; } TimerInfo that = (TimerInfo) other; return this.id == that.id && this.sink.equals(that.sink); } @Override public String toString() { return "TimerInfo [id=" + id + ", sink=" + sink + "]"; } } // Load of the poller. Currently, the number of file descriptors // registered. private final AtomicInteger load = new AtomicInteger(0); private final MultiMap timers = new MultiMap<>(); // the thread where all events will be dispatched. So, the actual IO or Reaper threads. protected final Thread worker; // did timers expiration add timer ? private boolean changed; protected PollerBase(String name) { worker = createWorker(name); } protected PollerBase(String name, BiFunction threadFactory) { worker = threadFactory.apply(this, name); } Thread createWorker(String name) { Thread worker = new Thread(this, name); worker.setDaemon(true); return worker; } long clock() { return Clock.nowMS(); } final boolean isEmpty() { return timers.isEmpty(); } // Returns load of the poller. Note that this function can be // invoked from a different thread! public final int getLoad() { return load.get(); } // Called by individual poller implementations to manage the load. protected void adjustLoad(int amount) { load.addAndGet(amount); } // Add a timeout to expire in timeout_ milliseconds. After the // expiration timerEvent on sink_ object will be called with // argument set to id_. public void addTimer(long timeout, IPollEvents sink, int id) { assert (Thread.currentThread() == worker); final long expiration = clock() + timeout; TimerInfo info = new TimerInfo(sink, id); timers.insert(expiration, info); changed = true; } // Cancel the timer created by sink_ object with ID equal to id_. public void cancelTimer(IPollEvents sink, int id) { assert (Thread.currentThread() == worker); TimerInfo copy = new TimerInfo(sink, id); // Complexity of this operation is O(n). We assume it is rarely used. TimerInfo timerInfo = timers.find(copy); if (timerInfo != null) { // let's defer the removal during the loop timerInfo.cancelled = true; } } // Executes any timers that are due. Returns number of milliseconds // to wait to match the next timer or 0 meaning "no timers". protected long executeTimers() { assert (Thread.currentThread() == worker); changed = false; // Fast track. if (timers.isEmpty()) { return 0L; } // Get the current time. long current = clock(); // Execute the timers that are already due. for (Entry entry : timers.entries()) { final TimerInfo timerInfo = entry.getKey(); if (timerInfo.cancelled) { timers.remove(entry.getValue(), timerInfo); continue; } // If we have to wait to execute the item, same will be true about // all the following items (multimap is sorted). Thus we can stop // checking the subsequent timers and return the time to wait for // the next timer (at least 1ms). final Long key = entry.getValue(); if (key > current) { return key - current; } // Remove it from the list of active timers. timers.remove(key, timerInfo); // Trigger the timer. timerInfo.sink.timerEvent(timerInfo.id); } // Remove empty list object for (Entry entry : timers.entries()) { final Long key = entry.getValue(); if (!timers.hasValues(key)) { timers.remove(key); } } if (changed) { return executeTimers(); } // There are no more timers. return 0L; } } jeromq-0.6.0/src/main/java/zmq/socket/000077500000000000000000000000001455771126300175605ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/Channel.java000066400000000000000000000061601455771126300217760ustar00rootroot00000000000000package zmq.socket; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.util.Blob; public class Channel extends SocketBase { private Pipe pipe; private Pipe lastIn; private Blob savedCredential; public Channel(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_CHANNEL; } @Override protected void destroy() { super.destroy(); assert (pipe == null); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); // ZMQ_PAIR socket can only be connected to a single peer. // The socket rejects any further connection requests. if (this.pipe == null) { this.pipe = pipe; } else { pipe.terminate(false); } } @Override protected void xpipeTerminated(Pipe pipe) { if (this.pipe == pipe) { if (lastIn == pipe) { savedCredential = lastIn.getCredential(); lastIn = null; } this.pipe = null; } } @Override protected void xreadActivated(Pipe pipe) { // There's just one pipe. No lists of active and inactive pipes. // There's nothing to do here. } @Override protected void xwriteActivated(Pipe pipe) { // There's just one pipe. No lists of active and inactive pipes. // There's nothing to do here. } @Override protected boolean xsend(Msg msg) { // CHANNEL sockets do not allow multipart data (ZMQ_SNDMORE) if (msg.hasMore()) { errno.set(ZError.EINVAL); return false; } if (pipe == null || !pipe.write(msg)) { errno.set(ZError.EAGAIN); return false; } pipe.flush(); return true; } @Override protected Msg xrecv() { if (pipe == null) { // Initialize the output parameter to be a 0-byte message. errno.set(ZError.EAGAIN); return null; } // Drop any messages with more flag Msg msg = pipe.read(); while (msg != null && msg.hasMore()) { // drop all frames of the current multi-frame message msg = pipe.read(); while (msg != null && msg.hasMore()) { msg = pipe.read(); } // get the new message if (msg != null) { msg = pipe.read(); } } if (msg == null) { errno.set(ZError.EAGAIN); return null; } lastIn = pipe; return msg; } @Override protected boolean xhasIn() { return pipe != null && pipe.checkRead(); } @Override protected boolean xhasOut() { return pipe != null && pipe.checkWrite(); } @Override protected Blob getCredential() { return lastIn != null ? lastIn.getCredential() : savedCredential; } } jeromq-0.6.0/src/main/java/zmq/socket/FQ.java000066400000000000000000000113541455771126300207350ustar00rootroot00000000000000package zmq.socket; import java.util.ArrayList; import java.util.Collections; import java.util.List; import zmq.Msg; import zmq.ZError; import zmq.pipe.Pipe; import zmq.util.Blob; import zmq.util.Errno; import zmq.util.ValueReference; // Class manages a set of inbound pipes. On receive it performs fair // queuing so that senders gone berserk won't cause denial of // service for decent senders. public class FQ { // Inbound pipes. private final List pipes; // Number of active pipes. All the active pipes are located at the // beginning of the pipes array. private int active; // the last pipe we received message from. // NULL when no message has been received or the pipe // has terminated. private Pipe lastIn; // Index of the next bound pipe to read a message from. private int current; // If true, part of a multipart message was already received, but // there are following parts still waiting in the current pipe. private boolean more; // Holds credential after the last_acive_pipe has terminated. private Blob savedCredential; public FQ() { active = 0; current = 0; more = false; pipes = new ArrayList<>(); } public void attach(Pipe pipe) { pipes.add(pipe); Collections.swap(pipes, active, pipes.size() - 1); active++; } public void terminated(Pipe pipe) { final int index = pipes.indexOf(pipe); // Remove the pipe from the list; adjust number of active pipes // accordingly. if (index < active) { active--; Collections.swap(pipes, index, active); if (current == active) { current = 0; } } pipes.remove(pipe); if (lastIn == pipe) { savedCredential = lastIn.getCredential(); lastIn = null; } } public void activated(Pipe pipe) { // Move the pipe to the list of active pipes. Collections.swap(pipes, pipes.indexOf(pipe), active); active++; } public Msg recv(Errno errno) { return recvPipe(errno, null); } public Msg recvPipe(Errno errno, ValueReference pipe) { // Round-robin over the pipes to get the next message. while (active > 0) { // Try to fetch new message. If we've already read part of the message // subsequent part should be immediately available. final Pipe currentPipe = pipes.get(current); final Msg msg = currentPipe.read(); final boolean fetched = msg != null; // Note that when message is not fetched, current pipe is deactivated // and replaced by another active pipe. Thus we don't have to increase // the 'current' pointer. if (fetched) { if (pipe != null) { pipe.set(currentPipe); } more = msg.hasMore(); if (!more) { lastIn = currentPipe; assert (active > 0); // happens when multiple threads receive messages current = (current + 1) % active; } return msg; } // Check the atomicity of the message. // If we've already received the first part of the message // we should get the remaining parts without blocking. assert (!more); active--; Collections.swap(pipes, current, active); if (current == active) { current = 0; } } // No message is available. Initialize the output parameter // to be a 0-byte message. errno.set(ZError.EAGAIN); return null; } public boolean hasIn() { // There are subsequent parts of the partly-read message available. if (more) { return true; } // Note that messing with current doesn't break the fairness of fair // queuing algorithm. If there are no messages available current will // get back to its original value. Otherwise it'll point to the first // pipe holding messages, skipping only pipes with no messages available. while (active > 0) { if (pipes.get(current).checkRead()) { return true; } // Deactivate the pipe. active--; Collections.swap(pipes, current, active); if (current == active) { current = 0; } } return false; } public Blob getCredential() { return lastIn != null ? lastIn.getCredential() : savedCredential; } } jeromq-0.6.0/src/main/java/zmq/socket/LB.java000066400000000000000000000072441455771126300207270ustar00rootroot00000000000000package zmq.socket; import java.util.ArrayList; import java.util.Collections; import java.util.List; import zmq.Msg; import zmq.ZError; import zmq.pipe.Pipe; import zmq.util.Errno; import zmq.util.ValueReference; public class LB { // List of outbound pipes. private final List pipes; // Number of active pipes. All the active pipes are located at the // beginning of the pipes array. private int active; // Points to the last pipe that the most recent message was sent to. private int current; // True if last we are in the middle of a multipart message. private boolean more; // True if we are dropping current message. private boolean dropping; public LB() { active = 0; current = 0; more = false; dropping = false; pipes = new ArrayList<>(); } public void attach(Pipe pipe) { pipes.add(pipe); activated(pipe); } public void terminated(Pipe pipe) { final int index = pipes.indexOf(pipe); // If we are in the middle of multipart message and current pipe // have disconnected, we have to drop the remainder of the message. if (index == current && more) { dropping = true; } // Remove the pipe from the list; adjust number of active pipes // accordingly. if (index < active) { active--; Collections.swap(pipes, index, active); if (current == active) { current = 0; } } pipes.remove(pipe); } public void activated(Pipe pipe) { // Move the pipe to the list of active pipes. Collections.swap(pipes, pipes.indexOf(pipe), active); active++; } public boolean sendpipe(Msg msg, Errno errno, ValueReference pipe) { // Drop the message if required. If we are at the end of the message // switch back to non-dropping mode. if (dropping) { more = msg.hasMore(); dropping = more; // msg_.close(); return true; } while (active > 0) { if (pipes.get(current).write(msg)) { if (pipe != null) { pipe.set(pipes.get(current)); } break; } assert (!more); active--; if (current < active) { Collections.swap(pipes, current, active); } else { current = 0; } } // If there are no pipes we cannot send the message. if (active == 0) { errno.set(ZError.EAGAIN); return false; } // If it's final part of the message we can flush it downstream and // continue round-robining (load balance). more = msg.hasMore(); if (!more) { pipes.get(current).flush(); if (++current >= active) { current = 0; } } return true; } public boolean hasOut() { // If one part of the message was already written we can definitely // write the rest of the message. if (more) { return true; } while (active > 0) { // Check whether a pipe has room for another message. if (pipes.get(current).checkWrite()) { return true; } // Deactivate the pipe. active--; Collections.swap(pipes, current, active); if (current == active) { current = 0; } } return false; } } jeromq-0.6.0/src/main/java/zmq/socket/Pair.java000066400000000000000000000053661455771126300213300ustar00rootroot00000000000000package zmq.socket; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.util.Blob; public class Pair extends SocketBase { private Pipe pipe; private Pipe lastIn; private Blob savedCredential; public Pair(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_PAIR; } @Override protected void destroy() { super.destroy(); assert (pipe == null); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); // ZMQ_PAIR socket can only be connected to a single peer. // The socket rejects any further connection requests. if (this.pipe == null) { this.pipe = pipe; } else { pipe.terminate(false); } } @Override protected void xpipeTerminated(Pipe pipe) { if (this.pipe == pipe) { if (lastIn == pipe) { savedCredential = lastIn.getCredential(); lastIn = null; } this.pipe = null; } } @Override protected void xreadActivated(Pipe pipe) { // There's just one pipe. No lists of active and inactive pipes. // There's nothing to do here. } @Override protected void xwriteActivated(Pipe pipe) { // There's just one pipe. No lists of active and inactive pipes. // There's nothing to do here. } @Override protected boolean xsend(Msg msg) { if (pipe == null || !pipe.write(msg)) { errno.set(ZError.EAGAIN); return false; } if (!msg.hasMore()) { pipe.flush(); } return true; } @Override protected Msg xrecv() { // Deallocate old content of the message. Msg msg; if (pipe == null) { // Initialize the output parameter to be a 0-byte message. errno.set(ZError.EAGAIN); return null; } else { msg = pipe.read(); if (msg == null) { // Initialize the output parameter to be a 0-byte message. errno.set(ZError.EAGAIN); return null; } } lastIn = pipe; return msg; } @Override protected boolean xhasIn() { return pipe != null && pipe.checkRead(); } @Override protected boolean xhasOut() { return pipe != null && pipe.checkWrite(); } @Override protected Blob getCredential() { return lastIn != null ? lastIn.getCredential() : savedCredential; } } jeromq-0.6.0/src/main/java/zmq/socket/Peer.java000066400000000000000000000012171455771126300213170ustar00rootroot00000000000000package zmq.socket; import zmq.Ctx; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.clientserver.Server; public class Peer extends Server { public Peer(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_PEER; options.canSendHelloMsg = true; options.canReceiveDisconnectMsg = true; options.canReceiveHiccupMsg = true; } @Override public void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { super.xattachPipe(pipe, subscribe2all, isLocallyInitiated); options.peerLastRoutingId = pipe.getRoutingId(); } } jeromq-0.6.0/src/main/java/zmq/socket/Raw.java000066400000000000000000000004411455771126300211530ustar00rootroot00000000000000package zmq.socket; import zmq.Ctx; import zmq.ZMQ; public class Raw extends Peer { public Raw(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_RAW; options.canSendHelloMsg = true; options.rawSocket = true; } } jeromq-0.6.0/src/main/java/zmq/socket/Sockets.java000066400000000000000000000147111455771126300220420ustar00rootroot00000000000000package zmq.socket; import java.util.Arrays; import java.util.List; import zmq.Ctx; import zmq.Options; import zmq.SocketBase; import zmq.io.HelloMsgSession; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.socket.pipeline.Pull; import zmq.socket.pipeline.Push; import zmq.socket.pubsub.Pub; import zmq.socket.pubsub.Sub; import zmq.socket.pubsub.XPub; import zmq.socket.pubsub.XSub; import zmq.socket.radiodish.Dish; import zmq.socket.radiodish.Radio; import zmq.socket.reqrep.Dealer; import zmq.socket.reqrep.Rep; import zmq.socket.reqrep.Req; import zmq.socket.reqrep.Router; import zmq.socket.clientserver.Server; import zmq.socket.clientserver.Client; import zmq.socket.scattergather.Gather; import zmq.socket.scattergather.Scatter; public enum Sockets { PAIR("PAIR") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Pair(parent, tid, sid); } }, PUB("SUB", "XSUB") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Pub(parent, tid, sid); } }, SUB("PUB", "XPUB") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Sub(parent, tid, sid); } }, REQ("REP", "ROUTER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Req(parent, tid, sid); } @Override public SessionBase create(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { return new Req.ReqSession(ioThread, connect, socket, options, addr); } }, REP("REQ", "DEALER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Rep(parent, tid, sid); } }, DEALER("REP", "DEALER", "ROUTER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Dealer(parent, tid, sid); } }, ROUTER("REQ", "DEALER", "ROUTER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Router(parent, tid, sid); } }, PULL("PUSH") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Pull(parent, tid, sid); } }, PUSH("PULL") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Push(parent, tid, sid); } }, XPUB("SUB", "XSUB") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new XPub(parent, tid, sid); } }, XSUB("PUB", "XPUB") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new XSub(parent, tid, sid); } }, STREAM { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Stream(parent, tid, sid); } }, SERVER("CLIENT") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Server(parent, tid, sid); } }, CLIENT("SERVER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Client(parent, tid, sid); } }, RADIO("DISH") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Radio(parent, tid, sid); } @Override public SessionBase create(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { return new Radio.RadioSession(ioThread, connect, socket, options, addr); } }, DISH("RADIO") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Dish(parent, tid, sid); } @Override public SessionBase create(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { return new Dish.DishSession(ioThread, connect, socket, options, addr); } }, CHANNEL("CHANNEL") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Channel(parent, tid, sid); } }, PEER("PEER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Peer(parent, tid, sid); } }, RAW { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Raw(parent, tid, sid); } }, SCATTER("GATHER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Scatter(parent, tid, sid); } }, GATHER("SCATTER") { @Override SocketBase create(Ctx parent, int tid, int sid) { return new Gather(parent, tid, sid); } }; private static final Sockets[] VALUES = values(); private final List compatible; Sockets(String... compatible) { this.compatible = Arrays.asList(compatible); } // Create a socket of a specified type. abstract SocketBase create(Ctx parent, int tid, int sid); public SessionBase create(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { if (options.canSendHelloMsg && options.helloMsg != null) { return new HelloMsgSession(ioThread, connect, socket, options, addr); } else { return new SessionBase(ioThread, connect, socket, options, addr); } } public static SessionBase createSession(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { return VALUES[options.type].create(ioThread, connect, socket, options, addr); } public static SocketBase create(int socketType, Ctx parent, int tid, int sid) { return VALUES[socketType].create(parent, tid, sid); } public static String name(int socketType) { return VALUES[socketType].name(); } public static Sockets fromType(int socketType) { return VALUES[socketType]; } public static boolean compatible(int self, String peer) { return VALUES[self].compatible.contains(peer); } } jeromq-0.6.0/src/main/java/zmq/socket/Stream.java000066400000000000000000000211421455771126300216560ustar00rootroot00000000000000package zmq.socket; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.Metadata; import zmq.pipe.Pipe; import zmq.util.Blob; import zmq.util.Utils; import zmq.util.ValueReference; import zmq.util.Wire; public class Stream extends SocketBase { // Fair queueing object for inbound pipes. private final FQ fq; // True if there is a message held in the pre-fetch buffer. private boolean prefetched; // If true, the receiver got the message part with // the peer's identity. private boolean identitySent; // Holds the prefetched identity. private Msg prefetchedId; // Holds the prefetched message. private Msg prefetchedMsg; private static class Outpipe { private final Pipe pipe; private boolean active; public Outpipe(Pipe pipe, boolean active) { this.pipe = pipe; this.active = active; } } // Outbound pipes indexed by the peer IDs. private final Map outpipes = new HashMap<>(); // The pipe we are currently writing to. private Pipe currentOut; // If true, more outgoing message parts are expected. private boolean moreOut; // Routing IDs are generated. It's a simple increment and wrap-over // algorithm. This value is the next ID to use (if not used already). private int nextRid; public Stream(Ctx parent, int tid, int sid) { super(parent, tid, sid); prefetched = false; identitySent = false; currentOut = null; moreOut = false; nextRid = Utils.randomInt(); options.type = ZMQ.ZMQ_STREAM; options.rawSocket = true; fq = new FQ(); prefetchedId = new Msg(); prefetchedMsg = new Msg(); } @Override protected void xattachPipe(Pipe pipe, boolean icanhasall, boolean isLocallyInitiated) { assert (pipe != null); identifyPeer(pipe, isLocallyInitiated); fq.attach(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { Outpipe outpipe = outpipes.remove(pipe.getIdentity()); assert (outpipe != null); fq.terminated(pipe); if (pipe == currentOut) { currentOut = null; } } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xwriteActivated(Pipe pipe) { Outpipe out = null; for (Outpipe outpipe : outpipes.values()) { if (outpipe.pipe == pipe) { out = outpipe; break; } } assert (out != null); assert (!out.active); out.active = true; } @Override protected boolean xsend(Msg msg) { // If this is the first part of the message it's the ID of the // peer to send the message to. if (!moreOut) { assert (currentOut == null); // If we have malformed message (prefix with no subsequent message) // then just silently ignore it. // TODO: The connections should be killed instead. if (msg.hasMore()) { moreOut = true; // Find the pipe associated with the identity stored in the prefix. // If there's no such pipe return an error Blob identity = Blob.createBlob(msg); Outpipe op = outpipes.get(identity); if (op != null) { currentOut = op.pipe; if (!currentOut.checkWrite()) { op.active = false; currentOut = null; errno.set(ZError.EAGAIN); return false; } } else { errno.set(ZError.EHOSTUNREACH); return false; } } // Expect one more message frame. moreOut = true; return true; } // Ignore the MORE flag msg.resetFlags(Msg.MORE); // This is the last part of the message. moreOut = false; // Push the message into the pipe. If there's no out pipe, just drop it. if (currentOut != null) { // Close the remote connection if user has asked to do so // by sending zero length message. // Pending messages in the pipe will be dropped (on receiving term- ack) if (msg.size() == 0) { currentOut.terminate(false); currentOut = null; return true; } boolean ok = currentOut.write(msg); if (ok) { currentOut.flush(); } currentOut = null; } return true; } @Override protected boolean xsetsockopt(int option, Object optval) { switch (option) { case ZMQ.ZMQ_CONNECT_RID: connectRid = (String) optval; return true; default: errno.set(ZError.EINVAL); return false; } } @Override public Msg xrecv() { Msg msg; if (prefetched) { if (!identitySent) { msg = prefetchedId; prefetchedId = null; identitySent = true; } else { msg = prefetchedMsg; prefetchedMsg = null; prefetched = false; } return msg; } ValueReference pipe = new ValueReference<>(); prefetchedMsg = fq.recvPipe(errno, pipe); // TODO DIFF V4 we sometimes need to process the commands to receive data, let's just return and give it another chance if (prefetchedMsg == null) { errno.set(ZError.EAGAIN); return null; } assert (pipe.get() != null); assert (!prefetchedMsg.hasMore()); // We have received a frame with TCP data. // Rather than sending this frame, we keep it in prefetched // buffer and send a frame with peer's ID. Blob identity = pipe.get().getIdentity(); msg = new Msg(identity.data()); // forward metadata (if any) Metadata metadata = prefetchedMsg.getMetadata(); if (metadata != null) { msg.setMetadata(metadata); } msg.setFlags(Msg.MORE); prefetched = true; identitySent = true; return msg; } @Override protected boolean xhasIn() { // We may already have a message pre-fetched. if (prefetched) { return true; } // Try to read the next message. // The message, if read, is kept in the pre-fetch buffer. ValueReference pipe = new ValueReference<>(); prefetchedMsg = fq.recvPipe(errno, pipe); if (prefetchedMsg == null) { return false; } assert (pipe.get() != null); assert (!prefetchedMsg.hasMore()); Blob identity = pipe.get().getIdentity(); prefetchedId = new Msg(identity.data()); // forward metadata (if any) Metadata metadata = prefetchedMsg.getMetadata(); if (metadata != null) { prefetchedId.setMetadata(metadata); } prefetchedId.setFlags(Msg.MORE); prefetched = true; identitySent = false; return true; } @Override protected boolean xhasOut() { // In theory, STREAM socket is always ready for writing. Whether actual // attempt to write succeeds depends on which pipe the message is going // to be routed to. return true; } private void identifyPeer(Pipe pipe, boolean isLocallyInitiated) { // Always assign identity for raw-socket Blob identity; if (connectRid != null && !connectRid.isEmpty() && isLocallyInitiated) { identity = Blob.createBlob(connectRid.getBytes(ZMQ.CHARSET)); connectRid = null; Outpipe outpipe = outpipes.get(identity); assert (outpipe == null); } else { ByteBuffer buf = ByteBuffer.allocate(5); buf.put((byte) 0); Wire.putUInt32(buf, nextRid++); identity = Blob.createBlob(buf.array()); } pipe.setIdentity(identity); // Add the record into output pipes lookup table Outpipe outpipe = new Outpipe(pipe, true); outpipes.put(identity, outpipe); } } jeromq-0.6.0/src/main/java/zmq/socket/clientserver/000077500000000000000000000000001455771126300222655ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/clientserver/Client.java000066400000000000000000000044731455771126300243560ustar00rootroot00000000000000package zmq.socket.clientserver; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.socket.LB; import zmq.util.Blob; public class Client extends SocketBase { // Messages are fair-queued from inbound pipes. And load-balanced to // the outbound pipes. private final FQ fq; private final LB lb; // Holds the prefetched message. public Client(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_CLIENT; options.canSendHelloMsg = true; options.canReceiveHiccupMsg = true; fq = new FQ(); lb = new LB(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); fq.attach(pipe); lb.attach(pipe); } @Override protected boolean xsend(Msg msg) { // CLIENT sockets do not allow multipart data (ZMQ_SNDMORE) if (msg.hasMore()) { errno.set(ZError.EINVAL); return false; } return lb.sendpipe(msg, errno, null); } @Override protected Msg xrecv() { Msg msg = fq.recvPipe(errno, null); // Drop any messages with more flag while (msg != null && msg.hasMore()) { // drop all frames of the current multi-frame message msg = fq.recvPipe(errno, null); while (msg != null && msg.hasMore()) { fq.recvPipe(errno, null); } // get the new message if (msg != null) { fq.recvPipe(errno, null); } } return msg; } @Override protected boolean xhasIn() { return fq.hasIn(); } @Override protected boolean xhasOut() { return lb.hasOut(); } @Override protected Blob getCredential() { return fq.getCredential(); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xwriteActivated(Pipe pipe) { lb.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); lb.terminated(pipe); } } jeromq-0.6.0/src/main/java/zmq/socket/clientserver/Server.java000066400000000000000000000116131455771126300244000ustar00rootroot00000000000000package zmq.socket.clientserver; import zmq.SocketBase; import zmq.Msg; import zmq.Ctx; import zmq.ZMQ; import zmq.ZError; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.util.Blob; import zmq.util.Utils; import zmq.util.ValueReference; import java.util.HashMap; import java.util.Map; //TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. public class Server extends SocketBase { // Fair queuing object for inbound pipes. private final FQ fq; static class Outpipe { private final Pipe pipe; private boolean active; public Outpipe(Pipe pipe, boolean active) { this.pipe = pipe; this.active = active; } } // Outbound pipes indexed by the peer IDs. private final Map outpipes; // Routing IDs are generated. It's a simple increment and wrap-over // algorithm. This value is the next ID to use (if not used already). private int nextRid; public Server(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); nextRid = Utils.randomInt(); options.type = ZMQ.ZMQ_SERVER; options.canSendHelloMsg = true; options.canReceiveDisconnectMsg = true; fq = new FQ(); outpipes = new HashMap<>(); } @Override protected void destroy() { assert (outpipes.isEmpty()); super.destroy(); } @Override public void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); int routingId = nextRid++; if (routingId == 0) { routingId = nextRid++; // Never use Routing ID zero } pipe.setRoutingId(routingId); // Add the record into output pipes lookup table Outpipe outpipe = new Outpipe(pipe, true); Outpipe prev = outpipes.put(routingId, outpipe); assert (prev == null); fq.attach(pipe); } @Override public void xpipeTerminated(Pipe pipe) { Outpipe old = outpipes.remove(pipe.getRoutingId()); assert (old != null); fq.terminated(pipe); } @Override public void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override public void xwriteActivated(Pipe pipe) { Outpipe out = outpipes.get(pipe.getRoutingId()); assert (out != null); assert (!out.active); out.active = true; } @Override protected boolean xsend(Msg msg) { // SERVER sockets do not allow multipart data (ZMQ_SNDMORE) if (msg.hasMore()) { errno.set(ZError.EINVAL); return false; } // Find the pipe associated with the routing stored in the message. int routingId = msg.getRoutingId(); Outpipe out = outpipes.get(routingId); if (out != null) { if (!out.pipe.checkWrite()) { out.active = false; errno.set(ZError.EAGAIN); return false; } } else { errno.set(ZError.EHOSTUNREACH); return false; } // Message might be delivered over inproc, so we reset routing id msg.resetRoutingId(); boolean ok = out.pipe.write(msg); if (ok) { out.pipe.flush(); } return true; } @Override protected Msg xrecv() { Msg msg; ValueReference pipe = new ValueReference<>(); msg = fq.recvPipe(errno, pipe); // Drop any messages with more flag while (msg != null && msg.hasMore()) { // drop all frames of the current multi-frame message msg = fq.recvPipe(errno, null); while (msg != null && msg.hasMore()) { msg = fq.recvPipe(errno, null); } // get the new message if (msg != null) { msg = fq.recvPipe(errno, pipe); } } if (msg == null) { return msg; } assert (pipe.get() != null); int routingId = pipe.get().getRoutingId(); msg.setRoutingId(routingId); return msg; } @Override protected boolean xhasIn() { return fq.hasIn(); } @Override protected boolean xhasOut() { // In theory, ROUTER socket is always ready for writing. Whether actual // attempt to write succeeds depends on whitch pipe the message is going // to be routed to. return true; } @Override protected Blob getCredential() { return fq.getCredential(); } @Override protected boolean xdisconnectPeer(int routingId) { Outpipe out = outpipes.get(routingId); if (out != null) { out.pipe.terminate(false); return true; } errno.set(ZError.EHOSTUNREACH); return false; } } jeromq-0.6.0/src/main/java/zmq/socket/pipeline/000077500000000000000000000000001455771126300213655ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/pipeline/Pull.java000066400000000000000000000020641455771126300231460ustar00rootroot00000000000000package zmq.socket.pipeline; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.util.Blob; public class Pull extends SocketBase { // Fair queueing object for inbound pipes. private final FQ fq; public Pull(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_PULL; fq = new FQ(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); fq.attach(pipe); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); } @Override public Msg xrecv() { return fq.recv(errno); } @Override protected boolean xhasIn() { return fq.hasIn(); } @Override protected Blob getCredential() { return fq.getCredential(); } } jeromq-0.6.0/src/main/java/zmq/socket/pipeline/Push.java000066400000000000000000000021341455771126300231470ustar00rootroot00000000000000package zmq.socket.pipeline; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.LB; public class Push extends SocketBase { // Load balancer managing the outbound pipes. private final LB lb; public Push(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_PUSH; lb = new LB(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); // Don't delay pipe termination as there is no one // to receive the delimiter. pipe.setNoDelay(); lb.attach(pipe); } @Override protected void xwriteActivated(Pipe pipe) { lb.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { lb.terminated(pipe); } @Override public boolean xsend(Msg msg) { return lb.sendpipe(msg, errno, null); } @Override protected boolean xhasOut() { return lb.hasOut(); } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/000077500000000000000000000000001455771126300210605ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/pubsub/Dist.java000066400000000000000000000132521455771126300226310ustar00rootroot00000000000000package zmq.socket.pubsub; import java.util.ArrayList; import java.util.Collections; import java.util.List; import zmq.Msg; import zmq.pipe.Pipe; public class Dist { // List of outbound pipes. private final List pipes; // Number of all the pipes to send the next message to. private int matching; // Number of active pipes. All the active pipes are located at the // beginning of the pipes array. These are the pipes the messages // can be sent to at the moment. private int active; // Number of pipes eligible for sending messages to. This includes all // the active pipes plus all the pipes that we can in theory send // messages to (the HWM is not yet reached), but sending a message // to them would result in partial message being delivered, ie. message // with initial parts missing. private int eligible; // True if last we are in the middle of a multipart message. private boolean more; public Dist() { matching = 0; active = 0; eligible = 0; more = false; pipes = new ArrayList<>(); } // Adds the pipe to the distributor object. public void attach(Pipe pipe) { // If we are in the middle of sending a message, we'll add new pipe // into the list of eligible pipes. Otherwise we add it to the list // of active pipes. if (more) { pipes.add(pipe); Collections.swap(pipes, eligible, pipes.size() - 1); } else { pipes.add(pipe); Collections.swap(pipes, active, pipes.size() - 1); active++; } eligible++; } // Mark the pipe as matching. Subsequent call to sendToMatching // will send message also to this pipe. public void match(Pipe pipe) { int idx = pipes.indexOf(pipe); // If pipe is already matching do nothing. if (idx < matching) { return; } // If the pipe isn't eligible, ignore it. if (idx >= eligible) { return; } // Mark the pipe as matching. Collections.swap(pipes, idx, matching); matching++; } // Mark all pipes as non-matching. public void unmatch() { matching = 0; } // Removes the pipe from the distributor object. public void terminated(Pipe pipe) { // Remove the pipe from the list; adjust number of matching, active and/or // eligible pipes accordingly. if (pipes.indexOf(pipe) < matching) { Collections.swap(pipes, pipes.indexOf(pipe), matching - 1); matching--; } if (pipes.indexOf(pipe) < active) { Collections.swap(pipes, pipes.indexOf(pipe), active - 1); active--; } if (pipes.indexOf(pipe) < eligible) { Collections.swap(pipes, pipes.indexOf(pipe), eligible - 1); eligible--; } pipes.remove(pipe); } // Activates pipe that have previously reached high watermark. public void activated(Pipe pipe) { // Move the pipe from passive to eligible state. if (eligible < pipes.size()) { Collections.swap(pipes, pipes.indexOf(pipe), eligible); eligible++; } // If there's no message being sent at the moment, move it to // the active state. if (!more && active < pipes.size()) { Collections.swap(pipes, eligible - 1, active); active++; } } // Send the message to all the outbound pipes. public boolean sendToAll(Msg msg) { matching = active; return sendToMatching(msg); } // Send the message to the matching outbound pipes. public boolean sendToMatching(Msg msg) { // Is this end of a multipart message? boolean msgMore = msg.hasMore(); // Push the message to matching pipes. distribute(msg); // If multipart message is fully sent, activate all the eligible pipes. if (!msgMore) { active = eligible; } more = msgMore; return true; } // Put the message to all active pipes. private void distribute(Msg msg) { // If there are no matching pipes available, simply drop the message. if (matching == 0) { return; } // TODO isVsm // Push copy of the message to each matching pipe. for (int idx = 0; idx < matching; ++idx) { if (!write(pipes.get(idx), msg)) { --idx; // Retry last write because index will have been swapped } } } public boolean hasOut() { return true; } // Write the message to the pipe. Make the pipe inactive if writing // fails. In such a case false is returned. private boolean write(Pipe pipe, Msg msg) { if (!pipe.write(msg)) { Collections.swap(pipes, pipes.indexOf(pipe), matching - 1); matching--; Collections.swap(pipes, pipes.indexOf(pipe), active - 1); active--; Collections.swap(pipes, active, eligible - 1); eligible--; return false; } if (!msg.hasMore()) { pipe.flush(); } return true; } public boolean checkHwm() { for (int idx = 0; idx < matching; ++idx) { if (!pipes.get(idx).checkHwm()) { return false; } } return true; } int active() { return active; } int eligible() { return eligible; } int matching() { return matching; } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/Mtrie.java000066400000000000000000000277121455771126300230140ustar00rootroot00000000000000package zmq.socket.pubsub; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; import zmq.Msg; import zmq.pipe.Pipe; import zmq.util.Utils; //Multi-trie. Each node in the trie is a set of pointers to pipes. class Mtrie { private Set pipes; private int min; private int count; private int liveNodes; private Mtrie[] next; public interface IMtrieHandler { void invoke(Pipe pipe, byte[] data, int size, XPub arg); } public Mtrie() { min = 0; count = 0; liveNodes = 0; pipes = null; next = null; } final boolean addOnTop(Pipe pipe) { assert (pipe != null); return addHelper(null, 0, 0, pipe); } // Add key to the trie. Returns true if it's a new subscription // rather than a duplicate. public boolean add(Msg msg, Pipe pipe) { assert (msg != null); assert (pipe != null); return addHelper(msg, 1, msg.size() - 1, pipe); } private boolean addHelper(Msg msg, int start, int size, Pipe pipe) { // We are at the node corresponding to the prefix. We are done. if (size == 0) { boolean result = pipes == null; if (pipes == null) { pipes = new HashSet<>(); } pipes.add(pipe); return result; } assert (msg != null); byte c = msg.get(start); if (c < min || c >= min + count) { // The character is out of range of currently handled // characters. We have to extend the table. if (count == 0) { min = c; count = 1; next = null; } else if (count == 1) { int oldc = min; Mtrie oldp = next[0]; count = (min < c ? c - min : min - c) + 1; next = new Mtrie[count]; min = Math.min(min, c); next[oldc - min] = oldp; } else if (min < c) { // The new character is above the current character range. count = c - min + 1; next = realloc(next, count, true); } else { // The new character is below the current character range. count = (min + count) - c; next = realloc(next, count, false); min = c; } } // If next node does not exist, create one. if (count == 1) { if (next == null) { next = new Mtrie[1]; next[0] = new Mtrie(); ++liveNodes; //alloc_assert (next.node); } return next[0].addHelper(msg, start + 1, size - 1, pipe); } else { if (next[c - min] == null) { next[c - min] = new Mtrie(); ++liveNodes; //alloc_assert (next.table [c - min]); } return next[c - min].addHelper(msg, start + 1, size - 1, pipe); } } private Mtrie[] realloc(Mtrie[] table, int size, boolean ended) { return Utils.realloc(Mtrie.class, table, size, ended); } // Remove all subscriptions for a specific peer from the trie. // If there are no subscriptions left on some topics, invoke the // supplied callback function. public boolean rm(Pipe pipe, IMtrieHandler func, XPub pub) { assert (pipe != null); assert (func != null); return rmHelper(pipe, new byte[0], 0, 0, func, pub); } private boolean rmHelper(Pipe pipe, byte[] buff, int buffsize, int maxBuffSize, IMtrieHandler func, XPub pub) { // Remove the subscription from this node. if (pipes != null && pipes.remove(pipe)) { if (pipes.isEmpty()) { func.invoke(null, buff, buffsize, pub); pipes = null; } } // Adjust the buffer. if (buffsize >= maxBuffSize) { maxBuffSize = buffsize + 256; buff = Utils.realloc(buff, maxBuffSize); } // If there are no subnodes in the trie, return. if (count == 0) { return true; } // If there's one subnode (optimisation). if (count == 1) { buff[buffsize] = (byte) min; buffsize++; next[0].rmHelper(pipe, buff, buffsize, maxBuffSize, func, pub); // Prune the node if it was made redundant by the removal if (next[0].isRedundant()) { next = null; count = 0; --liveNodes; assert (liveNodes == 0); } return true; } // If there are multiple subnodes. // // New min non-null character in the node table after the removal int newMin = min + count - 1; // New max non-null character in the node table after the removal int newMax = min; for (int c = 0; c != count; c++) { buff[buffsize] = (byte) (min + c); if (next[c] != null) { next[c].rmHelper(pipe, buff, buffsize + 1, maxBuffSize, func, pub); // Prune redundant nodes from the mtrie if (next[c].isRedundant()) { next[c] = null; assert (liveNodes > 0); --liveNodes; } else { // The node is not redundant, so it's a candidate for being // the new min/max node. // // We loop through the node array from left to right, so the // first non-null, non-redundant node encountered is the new // minimum index. Conversely, the last non-redundant, non-null // node encountered is the new maximum index. if (c + min < newMin) { newMin = c + min; } if (c + min > newMax) { newMax = c + min; } } } } assert (count > 1); // Free the node table if it's no longer used. if (liveNodes == 0) { next = null; count = 0; } // Compact the node table if possible else if (liveNodes == 1) { // If there's only one live node in the table we can // switch to using the more compact single-node // representation assert (newMin == newMax); assert (newMin >= min && newMin < min + count); Mtrie node = next[newMin - min]; assert (node != null); next = new Mtrie[] { node }; count = 1; min = newMin; } else if (newMin > min || newMax < min + count - 1) { assert (newMax > newMin); Mtrie[] oldTable = next; assert (newMin > min || newMax < min + count - 1); assert (newMin >= min); assert (newMax <= min + count - 1); assert (newMax - newMin + 1 < count); count = newMax - newMin + 1; next = new Mtrie[count]; System.arraycopy(oldTable, (newMin - min), next, 0, count); min = newMin; } return true; } // Remove specific subscription from the trie. Return true is it was // actually removed rather than de-duplicated. public boolean rm(Msg msg, Pipe pipe) { assert (msg != null); assert (pipe != null); return rmHelper(msg, 1, msg.size() - 1, pipe); } private boolean rmHelper(Msg msg, int start, int size, Pipe pipe) { if (size == 0) { if (pipes != null) { boolean erased = pipes.remove(pipe); assert (erased); if (pipes.isEmpty()) { pipes = null; } } return pipes == null; } byte c = msg.get(start); if (count == 0 || c < min || c >= min + count) { return false; } Mtrie nextNode = count == 1 ? next[0] : next[c - min]; if (nextNode == null) { return false; } boolean ret = nextNode.rmHelper(msg, start + 1, size - 1, pipe); if (nextNode.isRedundant()) { assert (count > 0); if (count == 1) { next = null; count = 0; --liveNodes; assert (liveNodes == 0); } else { next[c - min] = null; assert (liveNodes > 1); --liveNodes; // Compact the table if possible if (liveNodes == 1) { // If there's only one live node in the table we can // switch to using the more compact single-node // representation int i; for (i = 0; i < count; ++i) { if (next[i] != null) { break; } } assert (i < count); min += i; count = 1; Mtrie old = next[i]; next = new Mtrie[] { old }; } else if (c == min) { // We can compact the table "from the left" int i; for (i = 1; i < count; ++i) { if (next[i] != null) { break; } } assert (i < count); min += i; count -= i; next = realloc(next, count, true); } else if (c == min + count - 1) { // We can compact the table "from the right" int i; for (i = 1; i < count; ++i) { if (next[count - 1 - i] != null) { break; } } assert (i < count); count -= i; next = realloc(next, count, false); } } } return ret; } // Signal all the matching pipes. public void match(ByteBuffer data, int size, IMtrieHandler func, XPub pub) { assert (data != null); assert (func != null); assert (pub != null); Mtrie current = this; int idx = 0; while (true) { // Signal the pipes attached to this node. if (current.pipes != null) { for (Pipe it : current.pipes) { func.invoke(it, null, 0, pub); } } // If we are at the end of the message, there's nothing more to match. if (size == 0) { break; } // If there are no subnodes in the trie, return. if (current.count == 0) { break; } byte c = data.get(idx); // If there's one subnode (optimisation). if (current.count == 1) { if (c != current.min) { break; } current = current.next[0]; idx++; size--; continue; } // If there are multiple subnodes. if (c < current.min || c >= current.min + current.count) { break; } if (current.next[c - current.min] == null) { break; } current = current.next[c - current.min]; idx++; size--; } } private boolean isRedundant() { return pipes == null && liveNodes == 0; } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/Pub.java000066400000000000000000000016061455771126300224540ustar00rootroot00000000000000package zmq.socket.pubsub; import zmq.Ctx; import zmq.Msg; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; public class Pub extends XPub { public Pub(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_PUB; } @Override protected void xattachPipe(Pipe pipe, boolean subscribeToAll, boolean isLocallyInitiated) { assert (pipe != null); // Don't delay pipe termination as there is no one // to receive the delimiter. pipe.setNoDelay(); super.xattachPipe(pipe, subscribeToAll, isLocallyInitiated); } @Override protected Msg xrecv() { errno.set(ZError.ENOTSUP); // Messages cannot be received from PUB socket. throw new UnsupportedOperationException(); } @Override protected boolean xhasIn() { return false; } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/Sub.java000066400000000000000000000032231455771126300224540ustar00rootroot00000000000000package zmq.socket.pubsub; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.ZError; import zmq.ZMQ; public class Sub extends XSub { public Sub(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_SUB; // Switch filtering messages on (as opposed to XSUB which where the // filtering is off). options.filter = true; } @Override public boolean xsetsockopt(int option, Object optval) { if (option != ZMQ.ZMQ_SUBSCRIBE && option != ZMQ.ZMQ_UNSUBSCRIBE) { errno.set(ZError.EINVAL); return false; } if (optval == null) { errno.set(ZError.EINVAL); return false; } byte[] val = Options.parseBytes(option, optval); // Create the subscription message. Msg msg = new Msg(val.length + 1); if (option == ZMQ.ZMQ_SUBSCRIBE) { msg.put((byte) 1); } else { // option = ZMQ.ZMQ_UNSUBSCRIBE msg.put((byte) 0); } msg.put(val); // Pass it further on in the stack. boolean rc = super.xsend(msg); if (!rc) { errno.set(ZError.EINVAL); throw new IllegalStateException("Failed to send subscribe/unsubscribe message"); } return true; } @Override protected boolean xsend(Msg msg) { errno.set(ZError.ENOTSUP); // Overload the XSUB's send. throw new UnsupportedOperationException(); } @Override protected boolean xhasOut() { // Overload the XSUB's send. return false; } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/Trie.java000066400000000000000000000224041455771126300226300ustar00rootroot00000000000000package zmq.socket.pubsub; import java.nio.ByteBuffer; import zmq.Msg; import zmq.pipe.Pipe; import zmq.util.Utils; class Trie { public interface ITrieHandler { void added(byte[] data, int size, Pipe arg); } private int refcnt; private byte min; private int count; private int liveNodes; Trie[] next; public Trie() { min = 0; count = 0; liveNodes = 0; refcnt = 0; next = null; } // Add key to the trie. Returns true if this is a new item in the trie // rather than a duplicate. public boolean add(Msg msg, int start, int size) { // We are at the node corresponding to the prefix. We are done. if (size == 0) { ++refcnt; return refcnt == 1; } assert (msg != null); byte c = msg.get(start); if (c < min || c >= min + count) { // The character is out of range of currently handled // characters. We have to extend the table. if (count == 0) { min = c; count = 1; next = null; } else if (count == 1) { byte oldc = min; Trie oldp = next[0]; count = (min < c ? c - min : min - c) + 1; next = new Trie[count]; min = (byte) Math.min(min, c); next[oldc - min] = oldp; } else if (min < c) { // The new character is above the current character range. count = c - min + 1; next = realloc(next, count, true); } else { // The new character is below the current character range. count = (min + count) - c; next = realloc(next, count, false); min = c; } } // If next node does not exist, create one. if (count == 1) { if (next == null) { next = new Trie[1]; next[0] = new Trie(); ++liveNodes; assert (liveNodes == 1); } return next[0].add(msg, start + 1, size - 1); } else { if (next[c - min] == null) { next[c - min] = new Trie(); ++liveNodes; assert (liveNodes > 1); } return next[c - min].add(msg, start + 1, size - 1); } } private Trie[] realloc(Trie[] table, int size, boolean ended) { return Utils.realloc(Trie.class, table, size, ended); } // Remove key from the trie. Returns true if the item is actually // removed from the trie. public boolean rm(Msg msg, int start, int size) { // TODO: Shouldn't an error be reported if the key does not exist? if (size == 0) { if (refcnt == 0) { return false; } refcnt--; return refcnt == 0; } assert (msg != null); byte c = msg.get(start); if (count == 0 || c < min || c >= min + count) { return false; } Trie nextNode = count == 1 ? next[0] : next[c - min]; if (nextNode == null) { return false; } boolean ret = nextNode.rm(msg, start + 1, size - 1); // Prune redundant nodes if (nextNode.isRedundant()) { //delete next_node; assert (count > 0); if (count == 1) { // The just pruned node was the only live node next = null; count = 0; --liveNodes; assert (liveNodes == 0); } else { next[c - min] = null; assert (liveNodes > 1); --liveNodes; // Compact the table if possible if (liveNodes == 1) { // We can switch to using the more compact single-node // representation since the table only contains one live node Trie node = null; // Since we always compact the table the pruned node must // either be the left-most or right-most ptr in the node // table if (c == min) { // The pruned node is the left-most node ptr in the // node table => keep the right-most node node = next[count - 1]; min += count - 1; } else if (c == min + count - 1) { // The pruned node is the right-most node ptr in the // node table => keep the left-most node node = next[0]; } assert (node != null); //free (next.table); next = new Trie[] { node }; count = 1; } else if (c == min) { // We can compact the table "from the left". // Find the left-most non-null node ptr, which we'll use as // our new min byte newMin = min; for (int i = 1; i < count; ++i) { if (next[i] != null) { newMin = (byte) (i + min); break; } } assert (newMin != min); assert (newMin > min); assert (count > newMin - min); count = count - (newMin - min); next = realloc(next, count, true); min = newMin; } else if (c == min + count - 1) { // We can compact the table "from the right". // Find the right-most non-null node ptr, which we'll use to // determine the new table size int newCount = count; for (int i = 1; i < count; ++i) { if (next[count - 1 - i] != null) { newCount = count - i; break; } } assert (newCount != count); count = newCount; next = realloc(next, count, false); } } } return ret; } // Check whether particular key is in the trie. public boolean check(ByteBuffer data) { assert (data != null); int size = data.limit(); // This function is on critical path. It deliberately doesn't use // recursion to get a bit better performance. Trie current = this; int start = 0; while (true) { // We've found a corresponding subscription! if (current.refcnt > 0) { return true; } // We've checked all the data and haven't found matching subscription. if (size == 0) { return false; } // If there's no corresponding slot for the first character // of the prefix, the message does not match. byte c = data.get(start); if (c < current.min || c >= current.min + current.count) { return false; } // Move to the next character. if (current.count == 1) { current = current.next[0]; } else { current = current.next[c - current.min]; if (current == null) { return false; } } start++; size--; } } // Apply the function supplied to each subscription in the trie. public void apply(ITrieHandler func, Pipe arg) { applyHelper(null, 0, 0, func, arg); } private void applyHelper(byte[] buff, int buffsize, int maxBuffsize, ITrieHandler func, Pipe pipe) { assert (func != null); assert (pipe != null); // If this node is a subscription, apply the function. if (refcnt > 0) { func.added(buff, buffsize, pipe); } // Adjust the buffer. if (buffsize >= maxBuffsize) { maxBuffsize = buffsize + 256; buff = Utils.realloc(buff, maxBuffsize); assert (buff != null); } assert (buff != null); // If there are no subnodes in the trie, return. if (count == 0) { return; } // If there's one subnode (optimization). if (count == 1) { buff[buffsize] = min; buffsize++; next[0].applyHelper(buff, buffsize, maxBuffsize, func, pipe); return; } // If there are multiple subnodes. for (int c = 0; c != count; c++) { buff[buffsize] = (byte) (min + c); if (next[c] != null) { next[c].applyHelper(buff, buffsize + 1, maxBuffsize, func, pipe); } } } private boolean isRedundant() { return refcnt == 0 && liveNodes == 0; } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/XPub.java000066400000000000000000000235031455771126300226040ustar00rootroot00000000000000package zmq.socket.pubsub; import java.util.ArrayDeque; import java.util.Deque; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.pubsub.Mtrie.IMtrieHandler; import zmq.util.Blob; public class XPub extends SocketBase { private static final class SendUnsubscription implements IMtrieHandler { @Override public void invoke(Pipe pipe, byte[] data, int size, XPub self) { self.sendUnsubscription(data, size); } } private static final class MarkAsMatching implements IMtrieHandler { @Override public void invoke(Pipe pipe, byte[] data, int size, XPub self) { self.markAsMatching(pipe); } } // List of all subscriptions mapped to corresponding pipes. private final Mtrie subscriptions; // List of manual subscriptions mapped to corresponding pipes. private final Mtrie manualSubscriptions; // Distributor of messages holding the list of outbound pipes. private final Dist dist; // If true, send all subscription messages upstream, not just // unique ones private boolean verboseSubs; // If true, send all unsubscription messages upstream, not just // unique ones private boolean verboseUnsubs; // True if we are in the middle of sending a multi-part message. private boolean more; // Drop messages if HWM reached, otherwise return with false private boolean lossy; // Subscriptions will not bed added automatically, only after calling set option with ZMQ_SUBSCRIBE or ZMQ_UNSUBSCRIBE private boolean manual; // Last pipe that sent subscription message, only used if xpub is on manual private Pipe lastPipe; // Pipes that sent subscriptions messages that have not yet been processed, only used if xpub is on manual private final Deque pendingPipes; // List of pending (un)subscriptions, ie. those that were already // applied to the trie, but not yet received by the user. private final Deque pendingData; private final Deque pendingFlags; private static final IMtrieHandler markAsMatching = new MarkAsMatching(); private static final IMtrieHandler sendUnsubscription = new SendUnsubscription(); public XPub(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_XPUB; verboseSubs = false; verboseUnsubs = false; more = false; lossy = true; manual = false; subscriptions = new Mtrie(); manualSubscriptions = new Mtrie(); dist = new Dist(); lastPipe = null; pendingPipes = new ArrayDeque<>(); pendingData = new ArrayDeque<>(); pendingFlags = new ArrayDeque<>(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribeToAll, boolean isLocallyInitiated) { assert (pipe != null); dist.attach(pipe); // If subscribe_to_all_ is specified, the caller would like to subscribe // to all data on this pipe, implicitly. if (subscribeToAll) { subscriptions.addOnTop(pipe); } // The pipe is active when attached. Let's read the subscriptions from // it, if any. xreadActivated(pipe); } @Override protected void xreadActivated(Pipe pipe) { // There are some subscriptions waiting. Let's process them. Msg sub; while ((sub = pipe.read()) != null) { // Apply the subscription to the trie boolean subscribe; int size = sub.size(); if (size > 0 && (sub.get(0) == 0 || sub.get(0) == 1)) { subscribe = sub.get(0) == 1; } else { // Process user message coming upstream from xsub socket pendingData.add(Blob.createBlob(sub)); pendingFlags.add(sub.flags()); continue; } if (manual) { // Store manual subscription to use on termination if (!subscribe) { manualSubscriptions.rm(sub, pipe); } else { manualSubscriptions.add(sub, pipe); } pendingPipes.add(pipe); // ZMTP 3.1 hack: we need to support sub/cancel commands, but // we can't give them back to userspace as it would be an API // breakage since the payload of the message is completely // different. Manually craft an old-style message instead. pendingData.add(Blob.createBlob(sub)); pendingFlags.add(0); } else { boolean notify; if (!subscribe) { notify = subscriptions.rm(sub, pipe) || verboseUnsubs; } else { notify = subscriptions.add(sub, pipe) || verboseSubs; } // If the request was a new subscription, or the subscription // was removed, or verbose mode is enabled, store it so that // it can be passed to the user on next recv call. if (options.type == ZMQ.ZMQ_XPUB && notify) { pendingData.add(Blob.createBlob(sub)); pendingFlags.add(0); } } } } @Override protected void xwriteActivated(Pipe pipe) { dist.activated(pipe); } @Override public boolean xsetsockopt(int option, Object optval) { if (option == ZMQ.ZMQ_XPUB_VERBOSE || option == ZMQ.ZMQ_XPUB_VERBOSER || option == ZMQ.ZMQ_XPUB_NODROP || option == ZMQ.ZMQ_XPUB_MANUAL) { if (option == ZMQ.ZMQ_XPUB_VERBOSE) { verboseSubs = Options.parseBoolean(option, optval); verboseUnsubs = false; } else if (option == ZMQ.ZMQ_XPUB_VERBOSER) { verboseSubs = Options.parseBoolean(option, optval); verboseUnsubs = verboseSubs; } else if (option == ZMQ.ZMQ_XPUB_NODROP) { lossy = !Options.parseBoolean(option, optval); } else if (option == ZMQ.ZMQ_XPUB_MANUAL) { manual = Options.parseBoolean(option, optval); } } else if (option == ZMQ.ZMQ_SUBSCRIBE && manual) { if (null != lastPipe) { String val = Options.parseString(option, optval); subscriptions.add(new Msg(val.getBytes()), lastPipe); } } else if (option == ZMQ.ZMQ_UNSUBSCRIBE && manual) { if (null != lastPipe) { String val = Options.parseString(option, optval); subscriptions.rm(new Msg(val.getBytes()), lastPipe); } } else { errno.set(ZError.EINVAL); return false; } return true; } @Override protected void xpipeTerminated(Pipe pipe) { if (manual) { manualSubscriptions.rm(pipe, sendUnsubscription, this); subscriptions.rm(pipe, (p, d, s, self)-> { }, this); } else { // Remove the pipe from the trie. If there are topics that nobody // is interested in anymore, send corresponding unsubscriptions // upstream. subscriptions.rm(pipe, sendUnsubscription, this); } dist.terminated(pipe); } private void markAsMatching(Pipe pipe) { dist.match(pipe); } @Override protected boolean xsend(Msg msg) { boolean msgMore = msg.hasMore(); // For the first part of multi-part message, find the matching pipes. if (!more) { subscriptions.match(msg.buf(), msg.size(), markAsMatching, this); } if (lossy || dist.checkHwm()) { // Send the message to all the pipes that were marked as matching // in the previous step. if (dist.sendToMatching(msg)) { // If we are at the end of multi-part message we can mark all the pipes // as non-matching. if (!msgMore) { dist.unmatch(); } more = msgMore; return true; // Yay, sent successfully } } else { errno.set(ZError.EAGAIN); } return false; } @Override protected boolean xhasOut() { return dist.hasOut(); } @Override protected Msg xrecv() { // If there is at least one if (pendingData.isEmpty()) { errno.set(ZError.EAGAIN); return null; } // User is reading a message, set lastPipe and remove it from the deque if (manual && !pendingPipes.isEmpty()) { lastPipe = pendingPipes.pollFirst(); } Blob first = pendingData.pollFirst(); Msg msg = new Msg(first.data()); int flags = pendingFlags.pollFirst(); msg.setFlags(flags); return msg; } @Override protected boolean xhasIn() { return !pendingData.isEmpty(); } private void sendUnsubscription(byte[] data, int size) { if (options.type != ZMQ.ZMQ_PUB) { // Place the unsubscription to the queue of pending (un)sunscriptions // to be retrieved by the user later on. byte[] unsub = new byte[size + 1]; unsub[0] = 0; System.arraycopy(data, 0, unsub, 1, size); pendingData.add(Blob.createBlob(unsub)); pendingFlags.add(0); if (manual) { lastPipe = null; pendingPipes.clear(); } } } } jeromq-0.6.0/src/main/java/zmq/socket/pubsub/XSub.java000066400000000000000000000155411455771126300226120ustar00rootroot00000000000000package zmq.socket.pubsub; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.socket.pubsub.Trie.ITrieHandler; import zmq.util.Blob; public class XSub extends SocketBase { private final class SendSubscription implements ITrieHandler { @Override public void added(byte[] data, int size, Pipe pipe) { sendSubscription(data, size, pipe); } } // Fair queuing object for inbound pipes. private final FQ fq; // Object for distributing the subscriptions upstream. private final Dist dist; // The repository of subscriptions. private final Trie subscriptions; // If true, 'message' contains a matching message to return on the // next recv call. private boolean hasMessage; private Msg message; // If true, part of a multipart message was already received, but // there are following parts still waiting. private boolean more; private final ITrieHandler sendSubscription = new SendSubscription(); public XSub(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_XSUB; hasMessage = false; more = false; // When socket is being closed down we don't want to wait till pending // subscription commands are sent to the wire. options.linger = 0; fq = new FQ(); dist = new Dist(); subscriptions = new Trie(); message = new Msg(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); fq.attach(pipe); dist.attach(pipe); // Send all the cached subscriptions to the new upstream peer. subscriptions.apply(sendSubscription, pipe); pipe.flush(); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xwriteActivated(Pipe pipe) { dist.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); dist.terminated(pipe); } @Override protected void xhiccuped(Pipe pipe) { // Send all the cached subscriptions to the hiccuped pipe. subscriptions.apply(sendSubscription, pipe); pipe.flush(); } @Override protected boolean xsend(Msg msg) { final int size = msg.size(); if (size > 0 && msg.get(0) == 1) { // Process subscribe message // This used to filter out duplicate subscriptions, // however this is already done on the XPUB side and // doing it here as well breaks ZMQ_XPUB_VERBOSE // when there are forwarding devices involved. subscriptions.add(msg, 1, size - 1); return dist.sendToAll(msg); } else if (size > 0 && msg.get(0) == 0) { // Process unsubscribe message if (subscriptions.rm(msg, 1, size - 1)) { return dist.sendToAll(msg); } } else { // User message sent upstream to XPUB socket return dist.sendToAll(msg); } return true; } @Override protected boolean xhasOut() { // Subscription can be added/removed anytime. return true; } @Override protected Msg xrecv() { Msg msg; // If there's already a message prepared by a previous call to zmq_poll, // return it straight ahead. if (hasMessage) { msg = message; hasMessage = false; more = msg.hasMore(); return msg; } // TODO: This can result in infinite loop in the case of continuous // stream of non-matching messages which breaks the non-blocking recv // semantics. while (true) { // Get a message using fair queuing algorithm. msg = fq.recv(errno); // If there's no message available, return immediately. // The same when error occurs. if (msg == null) { return null; } // Check whether the message matches at least one subscription. // Non-initial parts of the message are passed if (more || !options.filter || match(msg)) { more = msg.hasMore(); return msg; } // Message doesn't match. Pop any remaining parts of the message // from the pipe. while (msg.hasMore()) { msg = fq.recv(errno); assert (msg != null); } } } @Override protected boolean xhasIn() { // There are subsequent parts of the partly-read message available. if (more) { return true; } // If there's already a message prepared by a previous call to zmq_poll, // return straight ahead. if (hasMessage) { return true; } // TODO: This can result in infinite loop in the case of continuous // stream of non-matching messages. while (true) { // Get a message using fair queuing algorithm. message = fq.recv(errno); // If there's no message available, return immediately. // The same when error occurs. if (message == null) { assert (errno.is(ZError.EAGAIN)); return false; } // Check whether the message matches at least one subscription. if (!options.filter || match(message)) { hasMessage = true; return true; } // Message doesn't match. Pop any remaining parts of the message // from the pipe. while (message.hasMore()) { message = fq.recv(errno); assert (message != null); } } } @Override protected Blob getCredential() { return fq.getCredential(); } private boolean match(Msg msg) { return subscriptions.check(msg.buf()); } private boolean sendSubscription(byte[] data, int size, Pipe pipe) { // Create the subscription message. Msg msg = new Msg(size + 1); msg.put((byte) 1).put(data, 0, size); // Send it to the pipe. boolean sent = pipe.write(msg); // If we reached the SNDHWM, and thus cannot send the subscription, drop // the subscription message instead. This matches the behaviour of // setSocketOption(ZMQ_SUBSCRIBE, ...), which also drops subscriptions // when the SNDHWM is reached. // if (!sent) // msg.close (); return sent; } } jeromq-0.6.0/src/main/java/zmq/socket/radiodish/000077500000000000000000000000001455771126300215265ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/radiodish/Dish.java000066400000000000000000000167011455771126300232650ustar00rootroot00000000000000package zmq.socket.radiodish; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.socket.pubsub.Dist; public class Dish extends SocketBase { // Fair queueing object for inbound pipes. private final FQ fq; // Object for distributing the subscriptions upstream. private final Dist dist; // The repository of subscriptions. private final Set subscriptions; // If true, 'message' contains a matching message to return on the // next recv call. private Msg pendingMsg; // Holds the prefetched message. public Dish(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_DISH; // When socket is being closed down we don't want to wait till pending // subscription commands are sent to the wire. options.linger = 0; fq = new FQ(); dist = new Dist(); subscriptions = new HashSet<>(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); fq.attach(pipe); dist.attach(pipe); // Send all the cached subscriptions to the new upstream peer. sendSubscriptions(pipe); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xwriteActivated(Pipe pipe) { dist.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); dist.terminated(pipe); } @Override protected void xhiccuped(Pipe pipe) { // Send all the cached subscriptions to the hiccuped pipe. sendSubscriptions(pipe); } @Override protected boolean xjoin(String group) { if (group.length() > Msg.MAX_GROUP_LENGTH) { errno.set(ZError.EINVAL); return false; } // User cannot join same group twice if (!subscriptions.add(group)) { errno.set(ZError.EINVAL); return false; } Msg msg = new Msg(); msg.initJoin(); msg.setGroup(group); dist.sendToAll(msg); return true; } @Override protected boolean xleave(String group) { if (group.length() > Msg.MAX_GROUP_LENGTH) { errno.set(ZError.EINVAL); return false; } if (!subscriptions.remove(group)) { errno.set(ZError.EINVAL); return false; } Msg msg = new Msg(); msg.initLeave(); msg.setGroup(group); dist.sendToAll(msg); return true; } @Override protected boolean xsend(Msg msg) { errno.set(ZError.ENOTSUP); throw new UnsupportedOperationException(); } @Override protected Msg xrecv() { // If there's already a message prepared by a previous call to poll, // return it straight ahead. if (pendingMsg != null) { Msg msg = pendingMsg; pendingMsg = null; return msg; } return xxrecv(); } private Msg xxrecv() { // Get a message using fair queueing algorithm. Msg msg = fq.recv(errno); // If there's no message available, return immediately. // The same when error occurs. if (msg == null) { return null; } // Skip non matching messages while (!subscriptions.contains(msg.getGroup())) { msg = fq.recv(errno); if (msg == null) { return null; } } // Found a matching message return msg; } @Override protected boolean xhasIn() { // If there's already a message prepared by a previous call to zmq_poll, // return straight ahead. if (pendingMsg != null) { return true; } Msg msg = xxrecv(); if (msg == null) { return false; } // Matching message found pendingMsg = msg; return true; } @Override protected boolean xhasOut() { // Subscription can be added/removed anytime. return true; } private void sendSubscriptions(Pipe pipe) { for (String s : subscriptions) { Msg msg = new Msg(); msg.initJoin(); msg.setGroup(s); pipe.write(msg); } pipe.flush(); } public static class DishSession extends SessionBase { static final byte[] JOIN_BYTES = "\4JOIN".getBytes(StandardCharsets.US_ASCII); static final byte[] LEAVE_BYTES = "\5LEAVE".getBytes(StandardCharsets.US_ASCII); enum State { GROUP, BODY } private State state; private String group; public DishSession(IOThread ioThread, boolean connect, SocketBase socket, final Options options, final Address addr) { super(ioThread, connect, socket, options, addr); state = State.GROUP; group = ""; } @Override public boolean pushMsg(Msg msg) { switch (state) { case GROUP: if (!msg.hasMore()) { errno.set(ZError.EFAULT); return false; } if (msg.size() > Msg.MAX_GROUP_LENGTH) { errno.set(ZError.EFAULT); return false; } group = new String(msg.data(), StandardCharsets.US_ASCII); state = State.BODY; return true; case BODY: // Set the message group msg.setGroup(group); // Thread safe socket doesn't support multipart messages if (msg.hasMore()) { errno.set(ZError.EFAULT); return false; } // Push message to dish socket boolean rc = super.pushMsg(msg); if (rc) { state = State.GROUP; } return rc; default: throw new IllegalStateException(); } } @Override protected Msg pullMsg() { Msg msg = super.pullMsg(); if (msg == null) { return null; } if (!msg.isJoin() && !msg.isLeave()) { return msg; } Msg command; byte[] groupBytes = msg.getGroup().getBytes(StandardCharsets.US_ASCII); if (msg.isJoin()) { command = new Msg(groupBytes.length + 5); command.put(JOIN_BYTES); } else { command = new Msg(groupBytes.length + 6); command.put(LEAVE_BYTES); } command.setFlags(Msg.COMMAND); // Copy the group command.put(groupBytes); return command; } @Override protected void reset() { super.reset(); state = State.GROUP; } } } jeromq-0.6.0/src/main/java/zmq/socket/radiodish/Radio.java000066400000000000000000000140221455771126300234260ustar00rootroot00000000000000package zmq.socket.radiodish; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.pipe.Pipe; import zmq.socket.pubsub.Dist; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class Radio extends SocketBase { private final Map> subscriptions; private final Dist dist; public Radio(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_RADIO; subscriptions = new HashMap<>(); dist = new Dist(); } @Override public void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); pipe.setNoDelay(); dist.attach(pipe); xreadActivated(pipe); } @Override public void xreadActivated(Pipe pipe) { Msg msg = pipe.read(); while (msg != null) { if (msg.isJoin()) { if (! subscriptions.containsKey(msg.getGroup())) { subscriptions.put(msg.getGroup(), new ArrayList<>()); } List pipes = subscriptions.get(msg.getGroup()); pipes.add(pipe); } else if (msg.isLeave()) { List pipes = subscriptions.get(msg.getGroup()); if (pipes != null) { pipes.remove(pipe); if (pipes.isEmpty()) { subscriptions.remove(msg.getGroup()); } } } msg = pipe.read(); } } @Override public void xwriteActivated(Pipe pipe) { dist.activated(pipe); } @Override public void xpipeTerminated(Pipe pipe) { Iterator>> i = subscriptions.entrySet().iterator(); while (i.hasNext()) { Entry> entry = i.next(); entry.getValue().remove(pipe); if (entry.getValue().isEmpty()) { i.remove(); } } dist.terminated(pipe); } @Override protected boolean xsend(Msg msg) { // SERVER sockets do not allow multipart data (ZMQ_SNDMORE) if (msg.hasMore()) { errno.set(ZError.EINVAL); return false; } dist.unmatch(); List range = subscriptions.get(msg.getGroup()); if (range != null) { for (Pipe pipe : range) { dist.match(pipe); } } dist.sendToMatching(msg); return true; } @Override protected Msg xrecv() { errno.set(ZError.ENOTSUP); // Messages cannot be received from RADIO socket. throw new UnsupportedOperationException(); } @Override protected boolean xhasOut() { return dist.hasOut(); } public static class RadioSession extends SessionBase { enum State { GROUP, BODY } private State state; private Msg pending; public RadioSession(IOThread ioThread, boolean connect, SocketBase socket, final Options options, final Address addr) { super(ioThread, connect, socket, options, addr); state = State.GROUP; } @Override public boolean pushMsg(Msg msg) { if (msg.isCommand()) { byte commandNameSize = msg.get(0); if (msg.size() < commandNameSize + 1) { return super.pushMsg(msg); } byte[] data = msg.data(); String commandName = new String(data, 1, commandNameSize, StandardCharsets.US_ASCII); int groupLength; String group; Msg joinLeaveMsg = new Msg(); // Set the msg type to either JOIN or LEAVE if (commandName.equals("JOIN")) { groupLength = msg.size() - 5; group = new String(data, 5, groupLength, StandardCharsets.US_ASCII); joinLeaveMsg.initJoin(); } else if (commandName.equals("LEAVE")) { groupLength = msg.size() - 6; group = new String(data, 6, groupLength, StandardCharsets.US_ASCII); joinLeaveMsg.initLeave(); } // If it is not a JOIN or LEAVE just push the message else { return super.pushMsg(msg); } // Set the group joinLeaveMsg.setGroup(group); // Push the join or leave command msg = joinLeaveMsg; return super.pushMsg(msg); } return super.pushMsg(msg); } @Override protected Msg pullMsg() { Msg msg; switch (state) { case GROUP: pending = super.pullMsg(); if (pending == null) { return null; } // First frame is the group msg = new Msg(pending.getGroup().getBytes(StandardCharsets.US_ASCII)); msg.setFlags(Msg.MORE); // Next status is the body state = State.BODY; break; case BODY: msg = pending; state = State.GROUP; break; default: throw new IllegalStateException(); } return msg; } @Override protected void reset() { super.reset(); state = State.GROUP; } } } jeromq-0.6.0/src/main/java/zmq/socket/reqrep/000077500000000000000000000000001455771126300210565ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/reqrep/Dealer.java000066400000000000000000000050361455771126300231210ustar00rootroot00000000000000package zmq.socket.reqrep; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.socket.LB; import zmq.util.Blob; import zmq.util.ValueReference; public class Dealer extends SocketBase { // Messages are fair-queued from inbound pipes. And load-balanced to // the outbound pipes. private final FQ fq; private final LB lb; // if true, send an empty message to every connected router peer private boolean probeRouter; // Holds the prefetched message. public Dealer(Ctx parent, int tid, int sid) { super(parent, tid, sid); options.type = ZMQ.ZMQ_DEALER; options.canSendHelloMsg = true; options.canReceiveHiccupMsg = true; fq = new FQ(); lb = new LB(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); if (probeRouter) { Msg probe = new Msg(); pipe.write(probe); // assert (rc == 0) is not applicable here, since it is not a bug. pipe.flush(); } fq.attach(pipe); lb.attach(pipe); } @Override protected boolean xsetsockopt(int option, Object optval) { if (option == ZMQ.ZMQ_PROBE_ROUTER) { probeRouter = Options.parseBoolean(option, optval); return true; } errno.set(ZError.EINVAL); return false; } @Override protected boolean xsend(Msg msg) { return sendpipe(msg, null); } @Override protected Msg xrecv() { return recvpipe(null); } @Override protected boolean xhasIn() { return fq.hasIn(); } @Override protected boolean xhasOut() { return lb.hasOut(); } @Override protected Blob getCredential() { return fq.getCredential(); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xwriteActivated(Pipe pipe) { lb.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); lb.terminated(pipe); } protected final boolean sendpipe(Msg msg, ValueReference pipe) { return lb.sendpipe(msg, errno, pipe); } protected final Msg recvpipe(ValueReference pipe) { return fq.recvPipe(errno, pipe); } } jeromq-0.6.0/src/main/java/zmq/socket/reqrep/Rep.java000066400000000000000000000057511455771126300224570ustar00rootroot00000000000000package zmq.socket.reqrep; import zmq.Ctx; import zmq.Msg; import zmq.ZError; import zmq.ZMQ; public class Rep extends Router { // If true, we are in process of sending the reply. If false we are // in process of receiving a request. private boolean sendingReply; // If true, we are starting to receive a request. The beginning // of the request is the backtrace stack. private boolean requestBegins; public Rep(Ctx parent, int tid, int sid) { super(parent, tid, sid); sendingReply = false; requestBegins = true; options.type = ZMQ.ZMQ_REP; options.canSendHelloMsg = false; } @Override protected boolean xsend(Msg msg) { // If we are in the middle of receiving a request, we cannot send reply. if (!sendingReply) { errno.set(ZError.EFSM); return false; } boolean more = msg.hasMore(); // Push message to the reply pipe. boolean rc = super.xsend(msg); if (!rc) { return false; } // If the reply is complete flip the FSM back to request receiving state. if (!more) { sendingReply = false; } return true; } @Override protected Msg xrecv() { // If we are in middle of sending a reply, we cannot receive next request. if (sendingReply) { errno.set(ZError.EFSM); } Msg msg; // First thing to do when receiving a request is to copy all the labels // to the reply pipe. if (requestBegins) { while (true) { msg = super.xrecv(); if (msg == null) { return null; } if (msg.hasMore()) { // Empty message part delimits the traceback stack. boolean bottom = (msg.size() == 0); // Push it to the reply pipe. boolean rc = super.xsend(msg); assert (rc); if (bottom) { break; } } else { // If the traceback stack is malformed, discard anything // already sent to pipe (we're at end of invalid message). super.rollback(); } } requestBegins = false; } // Get next message part to return to the user. msg = super.xrecv(); if (msg == null) { return null; } // If whole request is read, flip the FSM to reply-sending state. if (!msg.hasMore()) { sendingReply = true; requestBegins = true; } return msg; } @Override protected boolean xhasIn() { return !sendingReply && super.xhasIn(); } @Override protected boolean xhasOut() { return sendingReply && super.xhasOut(); } } jeromq-0.6.0/src/main/java/zmq/socket/reqrep/Req.java000066400000000000000000000215411455771126300224530ustar00rootroot00000000000000package zmq.socket.reqrep; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.IOThread; import zmq.io.SessionBase; import zmq.io.net.Address; import zmq.pipe.Pipe; import zmq.util.Utils; import zmq.util.ValueReference; import zmq.util.Wire; public class Req extends Dealer { // If true, request was already sent and reply wasn't received yet or // was received partially. private boolean receivingReply; // If true, we are starting to send/recv a message. The first part // of the message must be empty message part (backtrace stack bottom). private boolean messageBegins; // The pipe the request was sent to and where the reply is expected. private final ValueReference replyPipe = new ValueReference<>(); // Whether request id frames shall be sent and expected. private boolean requestIdFramesEnabled; // The current request id. It is incremented every time before a new // request is sent. private int requestId; // If false, send() will reset its internal state and terminate the // reply_pipe's connection instead of failing if a previous request is // still pending. private boolean strict; public Req(Ctx parent, int tid, int sid) { super(parent, tid, sid); receivingReply = false; messageBegins = true; options.type = ZMQ.ZMQ_REQ; options.canSendHelloMsg = false; requestIdFramesEnabled = false; requestId = Utils.randomInt(); strict = true; } @Override public boolean xsend(final Msg msg) { // If we've sent a request and we still haven't got the reply, // we can't send another request. if (receivingReply) { if (strict) { errno.set(ZError.EFSM); return false; } receivingReply = false; messageBegins = true; } // First part of the request is the request identity. if (messageBegins) { replyPipe.set(null); if (requestIdFramesEnabled) { requestId++; final Msg id = new Msg(4); Wire.putUInt32(id.buf(), requestId); id.setFlags(Msg.MORE); boolean rc = super.sendpipe(id, replyPipe); if (!rc) { return false; } } Msg bottom = new Msg(); bottom.setFlags(Msg.MORE); boolean rc = super.sendpipe(bottom, replyPipe); if (!rc) { return false; } assert (replyPipe.get() != null); messageBegins = false; // Eat all currently available messages before the request is fully // sent. This is done to avoid: // REQ sends request to A, A replies, B replies too. // A's reply was first and matches, that is used. // An hour later REQ sends a request to B. B's old reply is used. while (true) { Msg drop = super.xrecv(); if (drop == null) { break; } } } boolean more = msg.hasMore(); boolean rc = super.xsend(msg); if (!rc) { return false; } // If the request was fully sent, flip the FSM into reply-receiving state. if (!more) { receivingReply = true; messageBegins = true; } return true; } @Override protected Msg xrecv() { // If request wasn't send, we can't wait for reply. // Thus, we don't look at the state of the ZMQ_REQ_RELAXED option. if (!receivingReply) { errno.set(ZError.EFSM); return null; } // Skip messages until one with the right first frames is found. while (messageBegins) { // If enabled, the first frame must have the correct request_id. if (requestIdFramesEnabled) { Msg msg = recvReplyPipe(); if (msg == null) { return null; } if (!msg.hasMore() || msg.size() != 4 || msg.getInt(0) != requestId) { // Skip the remaining frames and try the next message while (msg.hasMore()) { msg = recvReplyPipe(); assert (msg != null); } continue; } } // The next frame must be 0. // TODO: Failing this check should also close the connection with the peer! Msg msg = recvReplyPipe(); if (msg == null) { return null; } if (!msg.hasMore() || msg.size() != 0) { // Skip the remaining frames and try the next message while (msg.hasMore()) { msg = recvReplyPipe(); assert (msg != null); } continue; } messageBegins = false; } Msg msg = recvReplyPipe(); if (msg == null) { return null; } // If the reply is fully received, flip the FSM into request-sending state. if (!msg.hasMore()) { receivingReply = false; messageBegins = true; } return msg; } @Override public boolean xhasIn() { // TODO: Duplicates should be removed here. return receivingReply && super.xhasIn(); } @Override public boolean xhasOut() { return !receivingReply && super.xhasOut(); } @Override protected boolean xsetsockopt(int option, Object optval) { switch (option) { case ZMQ.ZMQ_REQ_CORRELATE: requestIdFramesEnabled = Options.parseBoolean(option, optval); return true; case ZMQ.ZMQ_REQ_RELAXED: strict = !Options.parseBoolean(option, optval); return true; default: break; } return super.xsetsockopt(option, optval); } @Override protected void xpipeTerminated(Pipe pipe) { if (replyPipe.get() == pipe) { replyPipe.set(null); } super.xpipeTerminated(pipe); } private Msg recvReplyPipe() { while (true) { ValueReference pipe = new ValueReference<>(); Msg msg = super.recvpipe(pipe); if (msg == null) { return null; } if (replyPipe.get() == null || replyPipe.get() == pipe.get()) { return msg; } } } public static class ReqSession extends SessionBase { enum State { BOTTOM, REQUEST_ID, BODY } private State state; public ReqSession(IOThread ioThread, boolean connect, SocketBase socket, final Options options, final Address addr) { super(ioThread, connect, socket, options, addr); state = State.BOTTOM; } @Override public boolean pushMsg(Msg msg) { // Ignore commands, they are processed by the engine and should not // affect the state machine. if (msg.isCommand()) { return true; } switch (state) { case BOTTOM: if (msg.hasMore()) { // In case option ZMQ_CORRELATE is on, allow request_id to be // transfered as first frame (would be too cumbersome to check // whether the option is actually on or not). if (msg.size() == 4) { state = State.REQUEST_ID; return super.pushMsg(msg); } else if (msg.size() == 0) { state = State.BODY; return super.pushMsg(msg); } } break; case REQUEST_ID: if (msg.hasMore() && msg.size() == 0) { state = State.BODY; return super.pushMsg(msg); } break; case BODY: if (msg.hasMore()) { return super.pushMsg(msg); } if (msg.flags() == 0) { state = State.BOTTOM; return super.pushMsg(msg); } break; default: break; } errno.set(ZError.EFAULT); return false; } @Override public void reset() { super.reset(); state = State.BOTTOM; } } } jeromq-0.6.0/src/main/java/zmq/socket/reqrep/Router.java000066400000000000000000000346671455771126300232210ustar00rootroot00000000000000package zmq.socket.reqrep; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import zmq.Ctx; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.util.Blob; import zmq.util.Utils; import zmq.util.ValueReference; import zmq.util.Wire; //TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. public class Router extends SocketBase { // Fair queuing object for inbound pipes. private final FQ fq; // True if there is a message held in the pre-fetch buffer. private boolean prefetched; // If true, the receiver got the message part with // the peer's identity. private boolean identitySent; // Holds the prefetched identity. private Msg prefetchedId; // Holds the prefetched message. private Msg prefetchedMsg; // If true, more incoming message parts are expected. private boolean moreIn; static class Outpipe { private final Pipe pipe; private boolean active; public Outpipe(Pipe pipe, boolean active) { this.pipe = pipe; this.active = active; } } // We keep a set of pipes that have not been identified yet. private final Set anonymousPipes; // Outbound pipes indexed by the peer IDs. private final Map outpipes; // The pipe we are currently writing to. private Pipe currentOut; // If true, more outgoing message parts are expected. private boolean moreOut; // Routing IDs are generated. It's a simple increment and wrap-over // algorithm. This value is the next ID to use (if not used already). private int nextRid; // If true, report EAGAIN to the caller instead of silently dropping // the message targeting an unknown peer. private boolean mandatory; private boolean rawSocket; // if true, send an empty message to every connected router peer private boolean probeRouter; // If true, the router will reassign an identity upon encountering a // name collision. The new pipe will take the identity, the old pipe // will be terminated. private boolean handover; public Router(Ctx parent, int tid, int sid) { super(parent, tid, sid); prefetched = false; identitySent = false; moreIn = false; currentOut = null; moreOut = false; nextRid = Utils.randomInt(); mandatory = false; // raw_sock functionality in ROUTER is deprecated rawSocket = false; handover = false; options.type = ZMQ.ZMQ_ROUTER; options.recvIdentity = true; options.rawSocket = false; options.canSendHelloMsg = true; options.canReceiveDisconnectMsg = true; fq = new FQ(); prefetchedId = new Msg(); prefetchedMsg = new Msg(); anonymousPipes = new HashSet<>(); outpipes = new HashMap<>(); } @Override protected void destroy() { assert (anonymousPipes.isEmpty()); assert (outpipes.isEmpty()); super.destroy(); } @Override public void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); if (probeRouter) { Msg probe = new Msg(); pipe.write(probe); // assert (rc) is not applicable here, since it is not a bug. pipe.flush(); } boolean identityOk = identifyPeer(pipe, isLocallyInitiated); if (identityOk) { fq.attach(pipe); } else { anonymousPipes.add(pipe); } } @Override public boolean xsetsockopt(int option, Object optval) { if (option == ZMQ.ZMQ_CONNECT_RID) { connectRid = Options.parseString(option, optval); return true; } if (option == ZMQ.ZMQ_ROUTER_RAW) { rawSocket = Options.parseBoolean(option, optval); if (rawSocket) { options.recvIdentity = false; options.rawSocket = true; } return true; } if (option == ZMQ.ZMQ_ROUTER_MANDATORY) { mandatory = Options.parseBoolean(option, optval); return true; } if (option == ZMQ.ZMQ_PROBE_ROUTER) { probeRouter = Options.parseBoolean(option, optval); return true; } if (option == ZMQ.ZMQ_ROUTER_HANDOVER) { handover = Options.parseBoolean(option, optval); return true; } errno.set(ZError.EINVAL); return false; } @Override public void xpipeTerminated(Pipe pipe) { if (!anonymousPipes.remove(pipe)) { Outpipe old = outpipes.remove(pipe.getIdentity()); assert (old != null); fq.terminated(pipe); if (pipe == currentOut) { currentOut = null; } } } @Override public void xreadActivated(Pipe pipe) { if (!anonymousPipes.contains(pipe)) { fq.activated(pipe); } else { boolean identityOk = identifyPeer(pipe, false); if (identityOk) { anonymousPipes.remove(pipe); fq.attach(pipe); } } } @Override public void xwriteActivated(Pipe pipe) { for (Outpipe out : outpipes.values()) { if (out.pipe == pipe) { assert (!out.active); out.active = true; break; } } } @Override protected boolean xsend(Msg msg) { // If this is the first part of the message it's the ID of the // peer to send the message to. if (!moreOut) { assert (currentOut == null); // If we have malformed message (prefix with no subsequent message) // then just silently ignore it. // TODO: The connections should be killed instead. if (msg.hasMore()) { moreOut = true; // Find the pipe associated with the identity stored in the prefix. // If there's no such pipe just silently ignore the message, unless // mandatory is set. Blob identity = Blob.createBlob(msg); Outpipe op = outpipes.get(identity); if (op != null) { currentOut = op.pipe; if (!currentOut.checkWrite()) { op.active = false; currentOut = null; if (mandatory) { moreOut = false; errno.set(ZError.EAGAIN); return false; } } } else if (mandatory) { moreOut = false; errno.set(ZError.EHOSTUNREACH); return false; } } return true; } // Ignore the MORE flag for raw-sock or assert? if (options.rawSocket) { msg.resetFlags(Msg.MORE); } // Check whether this is the last part of the message. moreOut = msg.hasMore(); // Push the message into the pipe. If there's no out pipe, just drop it. if (currentOut != null) { // Close the remote connection if user has asked to do so // by sending zero length message. // Pending messages in the pipe will be dropped (on receiving term- ack) if (rawSocket && msg.size() == 0) { currentOut.terminate(false); currentOut = null; return true; } boolean ok = currentOut.write(msg); if (!ok) { // Message failed to send - we must close it ourselves. currentOut = null; } else if (!moreOut) { currentOut.flush(); currentOut = null; } } return true; } @Override protected Msg xrecv() { Msg msg; if (prefetched) { if (!identitySent) { msg = prefetchedId; prefetchedId = null; identitySent = true; } else { msg = prefetchedMsg; prefetchedMsg = null; prefetched = false; } moreIn = msg.hasMore(); return msg; } ValueReference pipe = new ValueReference<>(); msg = fq.recvPipe(errno, pipe); // It's possible that we receive peer's identity. That happens // after reconnection. The current implementation assumes that // the peer always uses the same identity. // TODO: handle the situation when the peer changes its identity. while (msg != null && msg.isIdentity()) { msg = fq.recvPipe(errno, pipe); } if (msg == null) { return null; } assert (pipe.get() != null); // If we are in the middle of reading a message, just return the next part. if (moreIn) { moreIn = msg.hasMore(); } else { // We are at the beginning of a message. // Keep the message part we have in the prefetch buffer // and return the ID of the peer instead. prefetchedMsg = msg; prefetched = true; Blob identity = pipe.get().getIdentity(); msg = new Msg(identity.data()); msg.setFlags(Msg.MORE); identitySent = true; } return msg; } // Rollback any message parts that were sent but not yet flushed. protected boolean rollback() { if (currentOut != null) { currentOut.rollback(); currentOut = null; moreOut = false; } return true; } @Override protected boolean xhasIn() { // If we are in the middle of reading the messages, there are // definitely more parts available. if (moreIn) { return true; } // We may already have a message pre-fetched. if (prefetched) { return true; } // Try to read the next message. // The message, if read, is kept in the pre-fetch buffer. ValueReference pipe = new ValueReference<>(); prefetchedMsg = fq.recvPipe(errno, pipe); // It's possible that we receive peer's identity. That happens // after reconnection. The current implementation assumes that // the peer always uses the same identity. // TODO: handle the situation when the peer changes its identity. while (prefetchedMsg != null && prefetchedMsg.isIdentity()) { prefetchedMsg = fq.recvPipe(errno, pipe); } if (prefetchedMsg == null) { return false; } assert (pipe.get() != null); Blob identity = pipe.get().getIdentity(); prefetchedId = new Msg(identity.data()); prefetchedId.setFlags(Msg.MORE); prefetched = true; identitySent = false; return true; } @Override protected boolean xhasOut() { // In theory, ROUTER socket is always ready for writing. Whether actual // attempt to write succeeds depends on whitch pipe the message is going // to be routed to. return true; } @Override protected Blob getCredential() { return fq.getCredential(); } private boolean identifyPeer(Pipe pipe, boolean isLocallyInitiated) { Blob identity; if (connectRid != null && !connectRid.isEmpty() && isLocallyInitiated) { identity = Blob.createBlob(connectRid.getBytes(ZMQ.CHARSET)); connectRid = null; Outpipe outpipe = outpipes.get(identity); assert (outpipe == null); // Not allowed to duplicate an existing rid } else { if (options.rawSocket) { // Always assign identity for raw-socket ByteBuffer buffer = ByteBuffer.allocate(5); buffer.put((byte) 0); Wire.putUInt32(buffer, nextRid++); identity = Blob.createBlob(buffer.array()); } else { // Pick up handshake cases and also case where next identity is set Msg msg = pipe.read(); if (msg == null) { return false; } if (msg.size() == 0) { // Fall back on the auto-generation ByteBuffer buf = ByteBuffer.allocate(5); buf.put((byte) 0); Wire.putUInt32(buf, nextRid++); identity = Blob.createBlob(buf.array()); } else { identity = Blob.createBlob(msg); if (outpipes.containsKey(identity)) { if (!handover) { // Ignore peers with duplicate ID return false; } // We will allow the new connection to take over this // identity. Temporarily assign a new identity to the // existing pipe so we can terminate it asynchronously. ByteBuffer buf = ByteBuffer.allocate(5); buf.put((byte) 0); Wire.putUInt32(buf, nextRid++); Blob newIdentity = Blob.createBlob(buf.array()); // Remove the existing identity entry to allow the new // connection to take the identity. Outpipe existingOutpipe = outpipes.remove(identity); existingOutpipe.pipe.setIdentity(newIdentity); outpipes.put(newIdentity, existingOutpipe); existingOutpipe.pipe.terminate(true); } } } } pipe.setIdentity(identity); // Add the record into output pipes lookup table Outpipe outpipe = new Outpipe(pipe, true); outpipes.put(identity, outpipe); return true; } } jeromq-0.6.0/src/main/java/zmq/socket/scattergather/000077500000000000000000000000001455771126300224205ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/socket/scattergather/Gather.java000066400000000000000000000031151455771126300244750ustar00rootroot00000000000000package zmq.socket.scattergather; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.FQ; import zmq.util.Blob; public class Gather extends SocketBase { // Fair queueing object for inbound pipes. private final FQ fq; // Holds the prefetched message. public Gather(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_GATHER; fq = new FQ(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); fq.attach(pipe); } @Override protected Msg xrecv() { Msg msg = fq.recvPipe(errno, null); // Drop any messages with more flag while (msg != null && msg.hasMore()) { // drop all frames of the current multi-frame message msg = fq.recvPipe(errno, null); while (msg != null && msg.hasMore()) { fq.recvPipe(errno, null); } // get the new message if (msg != null) { fq.recvPipe(errno, null); } } return msg; } @Override protected boolean xhasIn() { return fq.hasIn(); } @Override protected Blob getCredential() { return fq.getCredential(); } @Override protected void xreadActivated(Pipe pipe) { fq.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { fq.terminated(pipe); } } jeromq-0.6.0/src/main/java/zmq/socket/scattergather/Scatter.java000066400000000000000000000025321455771126300246720ustar00rootroot00000000000000package zmq.socket.scattergather; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.pipe.Pipe; import zmq.socket.LB; public class Scatter extends SocketBase { // Load balancer managing the outbound pipes. private final LB lb; // Holds the prefetched message. public Scatter(Ctx parent, int tid, int sid) { super(parent, tid, sid, true); options.type = ZMQ.ZMQ_SCATTER; lb = new LB(); } @Override protected void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated) { assert (pipe != null); // Don't delay pipe termination as there is no one // to receive the delimiter. pipe.setNoDelay(); lb.attach(pipe); } @Override protected boolean xsend(Msg msg) { // SCATTER sockets do not allow multipart data (ZMQ_SNDMORE) if (msg.hasMore()) { errno.set(ZError.EINVAL); return false; } return lb.sendpipe(msg, errno, null); } @Override protected boolean xhasOut() { return lb.hasOut(); } @Override protected void xwriteActivated(Pipe pipe) { lb.activated(pipe); } @Override protected void xpipeTerminated(Pipe pipe) { lb.terminated(pipe); } } jeromq-0.6.0/src/main/java/zmq/util/000077500000000000000000000000001455771126300172455ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/util/Blob.java000066400000000000000000000020601455771126300207640ustar00rootroot00000000000000package zmq.util; import java.util.Arrays; import zmq.Msg; public class Blob { private final byte[] buf; private Blob(byte[] data) { buf = data; } private static Blob createBlob(byte[] data, boolean copy) { if (copy) { byte[] b = new byte[data.length]; System.arraycopy(data, 0, b, 0, data.length); return new Blob(b); } else { return new Blob(data); } } public static Blob createBlob(Msg msg) { return createBlob(msg.data(), true); } public static Blob createBlob(byte[] data) { return createBlob(data, false); } public int size() { return buf.length; } public byte[] data() { return buf; } @Override public boolean equals(Object t) { if (t instanceof Blob) { return Arrays.equals(buf, ((Blob) t).buf); } return false; } @Override public int hashCode() { return Arrays.hashCode(buf); } } jeromq-0.6.0/src/main/java/zmq/util/Clock.java000066400000000000000000000017461455771126300211530ustar00rootroot00000000000000package zmq.util; import java.util.concurrent.TimeUnit; public class Clock { // TSC timestamp of when last time measurement was made. // private long last_tsc; // Physical time corresponding to the TSC above (in milliseconds). // private long last_time; private Clock() { } /** * High precision timestamp in microseconds. */ public static long nowUS() { return TimeUnit.MICROSECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS); } /** * High precision timestamp in nanoseconds. */ public static long nowNS() { return System.nanoTime(); } // Low precision timestamp. In tight loops generating it can be // 10 to 100 times faster than the high precision timestamp. public static long nowMS() { return System.currentTimeMillis(); } // CPU's timestamp counter. Returns 0 if it's not available. public static long rdtsc() { return 0; } } jeromq-0.6.0/src/main/java/zmq/util/Draft.java000066400000000000000000000004531455771126300211520ustar00rootroot00000000000000package zmq.util; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Documents an API in DRAFT state. * All APIs marked with @Draft are subject to change at ANY time until declared stable. */ @Retention(RetentionPolicy.SOURCE) public @interface Draft { } jeromq-0.6.0/src/main/java/zmq/util/Errno.java000066400000000000000000000007731455771126300212040ustar00rootroot00000000000000package zmq.util; // Emulates the errno mechanism present in C++, in a per-thread basis. public final class Errno { private static final ThreadLocal local = ThreadLocal.withInitial(() -> 0); public int get() { return local.get(); } public void set(int errno) { local.set(errno); } public boolean is(int err) { return get() == err; } @Override public String toString() { return "Errno[" + get() + "]"; } } jeromq-0.6.0/src/main/java/zmq/util/MultiMap.java000066400000000000000000000073111455771126300216420ustar00rootroot00000000000000package zmq.util; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; // custom implementation of a collection mapping multiple values, tailored for use in the lib. // this class is definitely not thread-safe, and allows only one mapping per key-value // aka if the same value is correlated to a new key, the old mapping is removed. public final class MultiMap, V> { // sorts entries according to the natural order of the values private final class EntryComparator implements Comparator> { @Override public int compare(Entry first, Entry second) { return first.getValue().compareTo(second.getValue()); } } private final Comparator> comparator = new EntryComparator(); // where all the data will be put private final Map> data; // inverse mapping to speed-up the process private final Map inverse; public MultiMap() { data = new HashMap<>(); inverse = new HashMap<>(); } public void clear() { data.clear(); inverse.clear(); } public Collection> entries() { List> list = new ArrayList<>(inverse.entrySet()); list.sort(comparator); return list; } @Deprecated public Collection values() { return inverse.keySet(); } public boolean contains(V value) { return inverse.containsKey(value); } public K key(V value) { return inverse.get(value); } public V find(V copy) { K key = inverse.get(copy); if (key != null) { List list = data.get(key); return list.get(list.indexOf(copy)); } return null; } public boolean hasValues(K key) { List list = data.get(key); if (list == null) { return false; } return !list.isEmpty(); } public boolean isEmpty() { return inverse.isEmpty(); } private List getValues(K key) { return data.computeIfAbsent(key, k -> new ArrayList<>()); } public boolean insert(K key, V value) { K old = inverse.get(value); if (old != null) { boolean rc = removeData(old, value); assert rc; } boolean inserted = getValues(key).add(value); if (inserted) { inverse.put(value, key); } else { inverse.remove(value); } return inserted; } public Collection remove(K key) { List removed = data.remove(key); if (removed != null) { for (V val : removed) { inverse.remove(val); } } return removed; } public boolean remove(V value) { K key = inverse.remove(value); if (key != null) { return removeData(key, value); } return false; } public boolean remove(K key, V value) { boolean removed = removeData(key, value); if (removed) { inverse.remove(value); } return removed; } private boolean removeData(K key, V value) { boolean removed = false; List list = data.get(key); if (list != null) { removed = list.remove(value); if (list.isEmpty()) { data.remove(key); } } return removed; } @Override public String toString() { return data.toString(); } } jeromq-0.6.0/src/main/java/zmq/util/Objects.java000066400000000000000000000003751455771126300215060ustar00rootroot00000000000000package zmq.util; public class Objects { private Objects() { // no instantiation } public static T requireNonNull(T object, String msg) { Utils.checkArgument(object != null, msg); return object; } } jeromq-0.6.0/src/main/java/zmq/util/Timers.java000066400000000000000000000154151455771126300213610ustar00rootroot00000000000000package zmq.util; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import zmq.ZMQ; import zmq.util.function.Supplier; /** * Manages set of timers. *

* Timers can be added with a given interval, when the interval of time expires after addition, handler method is executed with given arguments. * Timer is repetitive and will be executed over time until canceled. *

* This is a DRAFT class, and may change without notice. */ @Draft public final class Timers { public static final class Timer { private final Timers parent; private long interval; private boolean alive = true; private final Handler handler; private final Object[] args; private Timer(Timers parent, long interval, Handler handler, Object... args) { assert (args != null); this.parent = parent; this.interval = interval; this.handler = handler; this.args = args; } /** * Changes the interval of the timer. * This method is slow, canceling existing and adding a new timer yield better performance. * @param interval the new interval of the timer. * @return true if set, otherwise false. */ public boolean setInterval(long interval) { if (alive) { this.interval = interval; return parent.insert(this); } return false; } /** * Reset the timer. * This method is slow, canceling existing and adding a new timer yield better performance. * @return true if reset, otherwise false. */ public boolean reset() { if (alive) { return parent.insert(this); } return false; } /** * Cancels a timer. * @return true if cancelled, otherwise false. */ public boolean cancel() { if (alive) { alive = false; return true; } return false; } } public interface Handler { void time(Object... args); } private final MultiMap timers = new MultiMap<>(); private final Supplier clock; public Timers() { this(() -> TimeUnit.NANOSECONDS.toMillis(Clock.nowNS())); } /** * Builds a new timer. *

* This constructor is for testing and is not intended to be used in production code. * @param clock the supplier of the current time in milliseconds. */ public Timers(Supplier clock) { this.clock = clock; } private long now() { return clock.get(); } private boolean insert(Timer timer) { return timers.insert(now() + timer.interval, timer); } /** * Add timer to the set, timer repeats forever, or until cancel is called. * @param interval the interval of repetition in milliseconds. * @param handler the callback called at the expiration of the timer. * @param args the optional arguments for the handler. * @return an opaque handle for further cancel. */ public Timer add(long interval, Handler handler, Object... args) { if (handler == null) { return null; } Utils.checkArgument(interval > 0, "Delay of a timer has to be strictly greater than 0"); final Timer timer = new Timer(this, interval, handler, args); final boolean rc = insert(timer); assert (rc); return timer; } /** * Changes the interval of the timer. * This method is slow, canceling existing and adding a new timer yield better performance. * @param timer the timer to change the interval to. * @return true if set, otherwise false. * @deprecated use {@link Timer#setInterval(long)} instead */ @Deprecated public boolean setInterval(Timer timer, long interval) { assert (timer.parent == this); return timer.setInterval(interval); } /** * Reset the timer. * This method is slow, canceling existing and adding a new timer yield better performance. * @param timer the timer to reset. * @return true if reset, otherwise false. * @deprecated use {@link Timer#reset()} instead */ @Deprecated public boolean reset(Timer timer) { assert (timer.parent == this); return timer.reset(); } /** * Cancel a timer. * @param timer the timer to cancel. * @return true if cancelled, otherwise false. * @deprecated use {@link Timer#cancel()} instead */ @Deprecated public boolean cancel(Timer timer) { assert (timer.parent == this); return timer.cancel(); } /** * Returns the time in millisecond until the next timer. * @return the time in millisecond until the next timer. */ public long timeout() { final long now = now(); for (Entry entry : entries()) { final Timer timer = entry.getKey(); final Long expiration = entry.getValue(); if (timer.alive) { // Live timer, lets return the timeout if (expiration - now > 0) { return expiration - now; } else { return 0; } } // Remove it from the list of active timers. timers.remove(expiration, timer); } // Wait forever as no timers are alive return -1; } /** * Execute the timers. * @return the number of timers triggered. */ public int execute() { int executed = 0; final long now = now(); for (Entry entry : entries()) { final Timer timer = entry.getKey(); final Long expiration = entry.getValue(); // Dead timer, lets remove it and continue if (!timer.alive) { // Remove it from the list of active timers. timers.remove(expiration, timer); continue; } // Map is ordered, if we have to wait for current timer we can stop. if (expiration - now > 0) { break; } insert(timer); timer.handler.time(timer.args); ++executed; } return executed; } Iterable> entries() { return timers.entries(); } public int sleepAndExecute() { long timeout = timeout(); while (timeout > 0) { ZMQ.msleep(timeout); timeout = timeout(); } return execute(); } } jeromq-0.6.0/src/main/java/zmq/util/Utils.java000066400000000000000000000132501455771126300212110ustar00rootroot00000000000000package zmq.util; import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.net.ServerSocket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import java.security.SecureRandom; import zmq.ZError; import zmq.io.net.Address; import zmq.io.net.tcp.TcpUtils; import zmq.util.function.Supplier; public class Utils { private static final ThreadLocal random = ThreadLocal.withInitial(SecureRandom::new); private Utils() { } public static int randomInt() { return random.get().nextInt(); } public static int randomInt(int bound) { return random.get().nextInt(bound); } public static byte[] randomBytes(int length) { byte[] bytes = new byte[length]; random.get().nextBytes(bytes); return bytes; } /** * Finds a string whose hashcode is the number in input. * * @param port the port to find String hashcode-equivalent of. Has to be positive or 0. * @return a String whose hashcode is the number in input. */ public static String unhash(int port) { return unhash(new StringBuilder(), port, 'z').toString(); } private static StringBuilder unhash(StringBuilder builder, int port, char boundary) { int div = port / 31; int remainder = port % 31; if (div <= boundary) { if (div != 0) { builder.append((char) div); } } else { unhash(builder, div, boundary); } builder.append((char) remainder); return builder; } public static int findOpenPort() throws IOException { try (ServerSocket tmpSocket = new ServerSocket(0, 0)) { return tmpSocket.getLocalPort(); } } public static void unblockSocket(SelectableChannel... channels) throws IOException { TcpUtils.unblockSocket(channels); } @SuppressWarnings("unchecked") public static T[] realloc(Class klass, T[] src, int size, boolean ended) { T[] dest; if (size > src.length) { dest = (T[]) Array.newInstance(klass, size); if (ended) { System.arraycopy(src, 0, dest, 0, src.length); } else { System.arraycopy(src, 0, dest, size - src.length, src.length); } } else if (size < src.length) { dest = (T[]) Array.newInstance(klass, size); if (ended) { System.arraycopy(src, src.length - size, dest, 0, size); } else { System.arraycopy(src, 0, dest, 0, size); } } else { dest = src; } return dest; } public static byte[] bytes(ByteBuffer buf) { byte[] d = new byte[buf.limit()]; buf.get(d); return d; } public static byte[] realloc(byte[] src, int size) { byte[] dest = new byte[size]; if (src != null) { System.arraycopy(src, 0, dest, 0, src.length); } return dest; } public static boolean delete(File path) { if (!path.exists()) { return false; } boolean ret = true; if (path.isDirectory()) { File[] files = path.listFiles(); if (files != null) { for (File f : files) { ret = ret && delete(f); } } } return ret && path.delete(); } /** * Resolve the remote address of the channel. * @param fd the channel, should be a TCP socket channel * @return a new {@link Address} * @throws ZError.IOException if the channel is closed or an I/O errors occurred * @throws IllegalArgumentException if the SocketChannel is not a TCP channel */ public static Address getPeerIpAddress(SocketChannel fd) { try { SocketAddress address = fd.getRemoteAddress(); return new Address(address); } catch (IOException e) { throw new ZError.IOException(e); } } /** * Resolve the local address of the channel. * @param fd the channel, should be a TCP socket channel * @return a new {@link Address} * @throws ZError.IOException if the channel is closed or an I/O errors occurred * @throws IllegalArgumentException if the SocketChannel is not a TCP channel */ public static Address getLocalIpAddress(SocketChannel fd) { try { SocketAddress address = fd.getLocalAddress(); return new Address(address); } catch (IOException e) { throw new ZError.IOException(e); } } public static String dump(ByteBuffer buffer, int pos, int limit) { int oldpos = buffer.position(); int oldlimit = buffer.limit(); buffer.limit(limit).position(pos); StringBuilder builder = new StringBuilder("["); for (int idx = buffer.position(); idx < buffer.limit(); ++idx) { builder.append(buffer.get(idx)); builder.append(','); } builder.append(']'); buffer.limit(oldlimit).position(oldpos); return builder.toString(); } public static void checkArgument(boolean expression, String errorMessage) { checkArgument(expression, () -> errorMessage); } public static void checkArgument(boolean expression, Supplier errorMessage) { if (!expression) { throw new IllegalArgumentException(errorMessage.get()); } } } jeromq-0.6.0/src/main/java/zmq/util/ValueReference.java000066400000000000000000000006721455771126300230100ustar00rootroot00000000000000package zmq.util; public class ValueReference { private V value; public ValueReference(V value) { this.value = value; } public ValueReference() { } public final V get() { return value; } public final void set(V value) { this.value = value; } @Override public String toString() { return value == null ? "null" : value.toString(); } } jeromq-0.6.0/src/main/java/zmq/util/Wire.java000066400000000000000000000137761455771126300210340ustar00rootroot00000000000000package zmq.util; import java.nio.ByteBuffer; import java.nio.charset.Charset; import org.zeromq.ZMQ; import zmq.Msg; // Helper functions to convert different integer // types to/from network byte order. public class Wire { private Wire() { } private static int getUInt8(ByteBuffer buf, int offset) { return buf.get(offset) & 0xff; } private static ByteBuffer putUInt8(ByteBuffer buf, int value) { buf.put((byte) (value & 0xff)); return buf; } // 2 bytes value public static int getUInt16(byte[] bytes) { return (bytes[0] & 0xff) << 8 | bytes[1] & 0xff; } public static int getUInt16(ByteBuffer buf, int offset) { return (buf.get(offset) & 0xff) << 8 | (buf.get(offset + 1) & 0xff); } public static byte[] putUInt16(int value) { assert (value >= 0); // it has to be an *unsigned* int byte[] bytes = new byte[2]; bytes[0] = (byte) ((value >>> 8) & 0xff); bytes[1] = (byte) ((value & 0xff)); return bytes; } public static Msg putUInt16(Msg msg, int value) { msg.put((byte) ((value >>> 8) & 0xff)); msg.put((byte) ((value & 0xff))); return msg; } public static ByteBuffer putUInt16(ByteBuffer buf, int value) { buf.put((byte) ((value >>> 8) & 0xff)); buf.put((byte) ((value & 0xff))); return buf; } // 4 bytes value public static int getUInt32(ByteBuffer buf) { return getUInt32(buf, 0); } public static int getUInt32(ByteBuffer buf, int offset) { return (buf.get(offset) & 0xff) << 24 | (buf.get(offset + 1) & 0xff) << 16 | (buf.get(offset + 2) & 0xff) << 8 | (buf.get(offset + 3) & 0xff); } public static int getUInt32(Msg msg, int offset) { return msg.getInt(offset); } public static int getUInt32(byte[] bytes, int offset) { return (bytes[offset] & 0xff) << 24 | (bytes[offset + 1] & 0xff) << 16 | (bytes[offset + 2] & 0xff) << 8 | (bytes[offset + 3] & 0xff); } public static ByteBuffer putUInt32(ByteBuffer buf, int value) { buf.put((byte) ((value >>> 24) & 0xff)); buf.put((byte) ((value >>> 16) & 0xff)); buf.put((byte) ((value >>> 8) & 0xff)); buf.put((byte) ((value & 0xff))); return buf; } public static byte[] putUInt32(int value) { assert (value >= 0); // it has to be an *unsigned* int byte[] bytes = new byte[4]; bytes[0] = (byte) ((value >>> 24) & 0xff); bytes[1] = (byte) ((value >>> 16) & 0xff); bytes[2] = (byte) ((value >>> 8) & 0xff); bytes[3] = (byte) ((value & 0xff)); return bytes; } public static Msg putUInt32(Msg msg, int value) { msg.put((byte) ((value >>> 24) & 0xff)); msg.put((byte) ((value >>> 16) & 0xff)); msg.put((byte) ((value >>> 8) & 0xff)); msg.put((byte) ((value & 0xff))); return msg; } // 8 bytes value public static ByteBuffer putUInt64(ByteBuffer buf, long value) { buf.put((byte) ((value >>> 56) & 0xff)); buf.put((byte) ((value >>> 48) & 0xff)); buf.put((byte) ((value >>> 40) & 0xff)); buf.put((byte) ((value >>> 32) & 0xff)); buf.put((byte) ((value >>> 24) & 0xff)); buf.put((byte) ((value >>> 16) & 0xff)); buf.put((byte) ((value >>> 8) & 0xff)); buf.put((byte) ((value) & 0xff)); return buf; } public static long getUInt64(ByteBuffer buf, int offset) { return (long) (buf.get(offset) & 0xff) << 56 | (long) (buf.get(offset + 1) & 0xff) << 48 | (long) (buf.get(offset + 2) & 0xff) << 40 | (long) (buf.get(offset + 3) & 0xff) << 32 | (long) (buf.get(offset + 4) & 0xff) << 24 | (long) (buf.get(offset + 5) & 0xff) << 16 | (long) (buf.get(offset + 6) & 0xff) << 8 | (long) buf.get(offset + 7) & 0xff; } public static long getUInt64(Msg msg, int offset) { return msg.getLong(offset); } // strings public static int putShortString(ByteBuffer buf, String value) { return putShortString(ZMQ.CHARSET, buf, value); } public static String getShortString(ByteBuffer buf, int offset) { return getShortString(ZMQ.CHARSET, buf, offset); } public static int putShortString(Charset charset, ByteBuffer buf, String value) { int length = value.length(); Utils.checkArgument(length < 256, "String must be strictly smaller than 256 characters"); putUInt8(buf, length); buf.put(value.getBytes(charset)); return length + 1; } public static String getShortString(Charset charset, ByteBuffer buf, int offset) { int length = getUInt8(buf, offset); return extractString(charset, buf, offset, length, 1); } public static int putLongString(ByteBuffer buf, String value) { return putLongString(ZMQ.CHARSET, buf, value); } public static String getLongString(ByteBuffer buf, int offset) { return getLongString(ZMQ.CHARSET, buf, offset); } public static int putLongString(Charset charset, ByteBuffer buf, String value) { int length = value.length(); Utils.checkArgument(length < 0x7fffffff, "String must be smaller than 2^31-1 characters"); Wire.putUInt32(buf, length); buf.put(value.getBytes(charset)); return length + 4; } public static String getLongString(Charset charset, ByteBuffer buf, int offset) { int length = Wire.getUInt32(buf, offset); return extractString(charset, buf, offset, length, 4); } private static String extractString(Charset charset, ByteBuffer buf, int offset, int length, int sizeOfSize) { byte[] text = new byte[length]; int old = buf.position(); buf.position(offset + sizeOfSize); buf.get(text, 0, length); buf.position(old); return new String(text, charset); } } jeromq-0.6.0/src/main/java/zmq/util/Z85.java000066400000000000000000000070551455771126300205050ustar00rootroot00000000000000package zmq.util; import java.nio.ByteBuffer; // Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding public class Z85 { private Z85() { } // Maps base 256 to base 85 private static final String encoder = "0123456789" + "abcdefghij" + "klmnopqrst" + "uvwxyzABCD" + "EFGHIJKLMN" + "OPQRSTUVWX" + "YZ.-:+=^!/" + "*?&<>()[]{" + "}@%$#"; // Maps base 85 to base 256 // We chop off lower 32 and higher 128 ranges private static final byte[] decoder = { 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00, 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47, 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00 }; // -------------------------------------------------------------------------- // Encode a binary frame as a string; destination string MUST be at least // size * 5 / 4 bytes long plus 1 byte for the null terminator. Returns // dest. Size must be a multiple of 4. // Returns NULL and sets errno = EINVAL for invalid input. public static String encode(byte[] data, int size) { if (size % 4 != 0) { return null; } StringBuilder builder = new StringBuilder(); int byteNbr = 0; long value = 0; while (byteNbr < size) { // Accumulate value in base 256 (binary) int d = data[byteNbr++] & 0xff; value = value * 256 + d; if (byteNbr % 4 == 0) { // Output value in base 85 int divisor = 85 * 85 * 85 * 85; while (divisor != 0) { int index = (int) (value / divisor % 85); builder.append(encoder.charAt(index)); divisor /= 85; } value = 0; } } assert (builder.length() == size * 5 / 4); return builder.toString(); } // -------------------------------------------------------------------------- // Decode an encoded string into a binary frame; dest must be at least // strlen (string) * 4 / 5 bytes long. Returns dest. strlen (string) // must be a multiple of 5. // Returns NULL and sets errno = EINVAL for invalid input. public static byte[] decode(String string) { if (string.length() % 5 != 0) { return null; } ByteBuffer buf = ByteBuffer.allocate(string.length() * 4 / 5); int byteNbr = 0; int charNbr = 0; int stringLen = string.length(); long value = 0; while (charNbr < stringLen) { // Accumulate value in base 85 value = value * 85 + (decoder[string.charAt(charNbr++) - 32] & 0xff); if (charNbr % 5 == 0) { // Output value in base 256 int divisor = 256 * 256 * 256; while (divisor != 0) { buf.put(byteNbr++, (byte) ((value / divisor) % 256)); divisor /= 256; } value = 0; } } assert (byteNbr == string.length() * 4 / 5); return buf.array(); } } jeromq-0.6.0/src/main/java/zmq/util/function/000077500000000000000000000000001455771126300210725ustar00rootroot00000000000000jeromq-0.6.0/src/main/java/zmq/util/function/BiFunction.java000066400000000000000000000013411455771126300237740ustar00rootroot00000000000000package zmq.util.function; /** * Represents a function that accepts two arguments and produces a result. * This is the two-arity specialization of {@link Function}. * *

This is a functional interface * whose functional method is {@link #apply(Object, Object)}. * * @param the type of the first argument to the function * @param the type of the second argument to the function * @param the type of the result of the function * * @see Function */ public interface BiFunction { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); } jeromq-0.6.0/src/main/java/zmq/util/function/Consumer.java000066400000000000000000000010451455771126300235300ustar00rootroot00000000000000package zmq.util.function; /** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * *

This is a functional interface * whose functional method is {@link #accept(Object)}. * * @param the type of the input to the operation * */ public interface Consumer { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); } jeromq-0.6.0/src/main/java/zmq/util/function/Function.java000066400000000000000000000010051455771126300235160ustar00rootroot00000000000000package zmq.util.function; /** * Represents a function that accepts one argument and produces a result. * *

This is a functional interface * whose functional method is {@link #apply(Object)}. * * @param the type of the input to the function * @param the type of the result of the function * */ public interface Function { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); } jeromq-0.6.0/src/main/java/zmq/util/function/Supplier.java000066400000000000000000000007051455771126300235420ustar00rootroot00000000000000package zmq.util.function; /** * Represents a supplier of results. * *

There is no requirement that a new or distinct result be returned each * time the supplier is invoked. * *

This is a functional interface * whose functional method is {@link #get()}. * * @param the type of results supplied by this supplier * */ public interface Supplier { /** * Gets a result. * * @return a result */ T get(); } jeromq-0.6.0/src/test/000077500000000000000000000000001455771126300145735ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/000077500000000000000000000000001455771126300155145ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/guide/000077500000000000000000000000001455771126300166115ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/guide/MDP.java000066400000000000000000000016611455771126300201000ustar00rootroot00000000000000package guide; import java.util.Arrays; import org.zeromq.ZFrame; import org.zeromq.ZMQ; /** * Majordomo Protocol definitions, Java version */ public enum MDP { /** * This is the version of MDP/Client we implement */ C_CLIENT("MDPC01"), /** * This is the version of MDP/Worker we implement */ W_WORKER("MDPW01"), // MDP/Server commands, as byte values W_READY(1), W_REQUEST(2), W_REPLY(3), W_HEARTBEAT(4), W_DISCONNECT(5); private final byte[] data; MDP(String value) { this.data = value.getBytes(ZMQ.CHARSET); } MDP(int value) { //watch for ints>255, will be truncated byte b = (byte) (value & 0xFF); this.data = new byte[] { b }; } public ZFrame newFrame() { return new ZFrame(data); } public boolean frameEquals(ZFrame frame) { return Arrays.equals(data, frame.getData()); } } jeromq-0.6.0/src/test/java/guide/ZHelper.java000066400000000000000000000034111455771126300210240ustar00rootroot00000000000000package guide; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; public class ZHelper { private static final Random rand = new Random(System.currentTimeMillis()); /** * Receives all message parts from socket, prints neatly * @param sock */ public static void dump(Socket sock) { System.out.println("----------------------------------------"); while (true) { byte[] msg = sock.recv(0); boolean isText = true; StringBuilder data = new StringBuilder(); for (byte b : msg) { if (b < 32 || b > 127) isText = false; data.append(String.format("%02X", b)); } if (isText) data = new StringBuilder(new String(msg, ZMQ.CHARSET)); System.out.printf("[%03d] %s%n", msg.length, data); if (!sock.hasReceiveMore()) break; } } public static void setId(Socket sock) { String identity = String.format("%04X-%04X", rand.nextInt(), rand.nextInt()); sock.setIdentity(identity.getBytes(ZMQ.CHARSET)); } public static List buildZPipe(Context ctx) { Socket socket1 = ctx.socket(SocketType.PAIR); socket1.setLinger(0); socket1.setHWM(1); Socket socket2 = ctx.socket(SocketType.PAIR); socket2.setLinger(0); socket2.setHWM(1); String iface = "inproc://" + new BigInteger(130, rand).toString(32); socket1.bind(iface); socket2.connect(iface); return Arrays.asList(socket1, socket2); } } jeromq-0.6.0/src/test/java/guide/asyncsrv.java000066400000000000000000000122451455771126300213300ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // //Asynchronous client-to-server (DEALER to ROUTER) // //While this example runs in a single process, that is just to make //it easier to start and stop the example. Each task has its own //context and conceptually acts as a separate process. public class asyncsrv { //--------------------------------------------------------------------- //This is our client task //It connects to the server, and then sends a request once per second //It collects responses as they arrive, and it prints them out. We will //run several client tasks in parallel, each with a different random ID. private static final Random rand = new Random(System.nanoTime()); private static class client_task implements Runnable { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket client = ctx.createSocket(SocketType.DEALER); // Set random identity to make tracing easier String identity = String.format( "%04X-%04X", rand.nextInt(), rand.nextInt() ); client.setIdentity(identity.getBytes(ZMQ.CHARSET)); client.connect("tcp://localhost:5570"); Poller poller = ctx.createPoller(1); poller.register(client, Poller.POLLIN); int requestNbr = 0; while (!Thread.currentThread().isInterrupted()) { // Tick once per second, pulling in arriving messages for (int centitick = 0; centitick < 100; centitick++) { poller.poll(10); if (poller.pollin(0)) { ZMsg msg = ZMsg.recvMsg(client); msg.getLast().print(identity); msg.destroy(); } } client.send(String.format("request #%d", ++requestNbr), 0); } } } } //This is our server task. //It uses the multithreaded server model to deal requests out to a pool //of workers and route replies back to clients. One worker can handle //one request at a time but one client can talk to multiple workers at //once. private static class server_task implements Runnable { @Override public void run() { try (ZContext ctx = new ZContext()) { // Frontend socket talks to clients over TCP Socket frontend = ctx.createSocket(SocketType.ROUTER); frontend.bind("tcp://*:5570"); // Backend socket talks to workers over inproc Socket backend = ctx.createSocket(SocketType.DEALER); backend.bind("inproc://backend"); // Launch pool of worker threads, precise number is not critical for (int threadNbr = 0; threadNbr < 5; threadNbr++) new Thread(new server_worker(ctx)).start(); // Connect backend to frontend via a proxy ZMQ.proxy(frontend, backend, null); } } } //Each worker task works on one request at a time and sends a random number //of replies back, with random delays between replies: private static class server_worker implements Runnable { private final ZContext ctx; public server_worker(ZContext ctx) { this.ctx = ctx; } @Override public void run() { Socket worker = ctx.createSocket(SocketType.DEALER); worker.connect("inproc://backend"); while (!Thread.currentThread().isInterrupted()) { // The DEALER socket gives us the address envelope and message ZMsg msg = ZMsg.recvMsg(worker); ZFrame address = msg.pop(); ZFrame content = msg.pop(); assert (content != null); msg.destroy(); // Send 0..4 replies back int replies = rand.nextInt(5); for (int reply = 0; reply < replies; reply++) { // Sleep for some fraction of a second try { Thread.sleep(rand.nextInt(1000) + 1); } catch (InterruptedException e) { } address.send(worker, ZFrame.REUSE + ZFrame.MORE); content.send(worker, ZFrame.REUSE); } address.destroy(); content.destroy(); } ctx.destroy(); } } //The main thread simply starts several clients, and a server, and then //waits for the server to finish. public static void main(String[] args) throws Exception { new Thread(new client_task()).start(); new Thread(new client_task()).start(); new Thread(new client_task()).start(); new Thread(new server_task()).start(); // Run for 5 seconds then quit Thread.sleep(5 * 1000); } } jeromq-0.6.0/src/test/java/guide/bstar.java000066400000000000000000000253001455771126300205670ustar00rootroot00000000000000package guide; import org.zeromq.*; import org.zeromq.ZLoop.IZLoopHandler; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Socket; // bstar class - Binary Star reactor public class bstar { // States we can be in at any point in time enum State { STATE_PRIMARY, // Primary, waiting for peer to connect STATE_BACKUP, // Backup, waiting for peer to connect STATE_ACTIVE, // Active - accepting connections STATE_PASSIVE // Passive - not accepting connections } // Events, which start with the states our peer can be in enum Event { PEER_PRIMARY, // HA peer is pending primary PEER_BACKUP, // HA peer is pending backup PEER_ACTIVE, // HA peer is active PEER_PASSIVE, // HA peer is passive CLIENT_REQUEST // Client makes request } private final ZContext ctx; // Our private context private final ZLoop loop; // Reactor loop private final Socket statepub; // State publisher private State state; // Current state private Event event; // Current event private long peerExpiry; // When peer is considered 'dead' private ZLoop.IZLoopHandler voterFn; // Voting socket handler private Object voterArg; // Arguments for voting handler private ZLoop.IZLoopHandler activeFn; // Call when become active private Object activeArg; // Arguments for handler private ZLoop.IZLoopHandler passiveFn; // Call when become passive private Object passiveArg; // Arguments for handler // The finite-state machine is the same as in the proof-of-concept server. // To understand this reactor in detail, first read the ZLoop class. // .skip // We send state information this often // If peer doesn't respond in two heartbeats, it is 'dead' private final static int BSTAR_HEARTBEAT = 1000; // In msecs // Binary Star finite state machine (applies event to state) // Returns false if there was an exception, true if event was valid. private boolean execute() { boolean rc = true; // Primary server is waiting for peer to connect // Accepts CLIENT_REQUEST events in this state if (state == State.STATE_PRIMARY) { if (event == Event.PEER_BACKUP) { System.out.print("I: connected to backup (passive), ready active\n"); state = State.STATE_ACTIVE; if (activeFn != null) activeFn.handle(loop, null, activeArg); } else if (event == Event.PEER_ACTIVE) { System.out.print("I: connected to backup (active), ready passive\n"); state = State.STATE_PASSIVE; if (passiveFn != null) passiveFn.handle(loop, null, passiveArg); } else if (event == Event.CLIENT_REQUEST) { // Allow client requests to turn us into the active if we've // waited sufficiently long to believe the backup is not // currently acting as active (i.e., after a failover) assert (peerExpiry > 0); if (System.currentTimeMillis() >= peerExpiry) { System.out.print("I: request from client, ready as active\n"); state = State.STATE_ACTIVE; if (activeFn != null) activeFn.handle(loop, null, activeArg); } else // Don't respond to clients yet - it's possible we're // performing a failback and the backup is currently active rc = false; } } else if (state == State.STATE_BACKUP) { if (event == Event.PEER_ACTIVE) { System.out.print("I: connected to primary (active), ready passive\n"); state = State.STATE_PASSIVE; if (passiveFn != null) passiveFn.handle(loop, null, passiveArg); } else // Reject client connections when acting as backup if (event == Event.CLIENT_REQUEST) rc = false; } else // .split active and passive states // These are the ACTIVE and PASSIVE states: if (state == State.STATE_ACTIVE) { if (event == Event.PEER_ACTIVE) { // Two actives would mean split-brain System.out.print("E: fatal error - dual actives, aborting\n"); rc = false; } } else // Server is passive // CLIENT_REQUEST events can trigger failover if peer looks dead if (state == State.STATE_PASSIVE) { if (event == Event.PEER_PRIMARY) { // Peer is restarting - become active, peer will go passive System.out.print("I: primary (passive) is restarting, ready active\n"); state = State.STATE_ACTIVE; } else if (event == Event.PEER_BACKUP) { // Peer is restarting - become active, peer will go passive System.out.print("I: backup (passive) is restarting, ready active\n"); state = State.STATE_ACTIVE; } else if (event == Event.PEER_PASSIVE) { // Two passives would mean cluster would be non-responsive System.out.print("E: fatal error - dual passives, aborting\n"); rc = false; } else if (event == Event.CLIENT_REQUEST) { // Peer becomes active if timeout has passed // It's the client request that triggers the failover assert (peerExpiry > 0); if (System.currentTimeMillis() >= peerExpiry) { // If peer is dead, switch to the active state System.out.print("I: failover successful, ready active\n"); state = State.STATE_ACTIVE; } else // If peer is alive, reject connections rc = false; // Call state change handler if necessary if (state == State.STATE_ACTIVE && activeFn != null) activeFn.handle(loop, null, activeArg); } } return rc; } private void updatePeerExpiry() { peerExpiry = System.currentTimeMillis() + 2 * BSTAR_HEARTBEAT; } // Reactor event handlers... // Publish our state to peer private static final IZLoopHandler SendState = (loop, item, arg) -> { bstar self = (bstar) arg; self.statepub.send(String.format("%d", self.state.ordinal())); return 0; }; // Receive state from peer, execute finite state machine private static final IZLoopHandler RecvState = (loop, item, arg) -> { bstar self = (bstar) arg; String state = item.getSocket().recvStr(); if (state != null) { self.event = Event.values()[Integer.parseInt(state)]; self.updatePeerExpiry(); } return self.execute() ? 0 : -1; }; // Application wants to speak to us, see if it's possible private static final IZLoopHandler VoterReady = (loop, item, arg) -> { bstar self = (bstar) arg; // If server can accept input now, call appl handler self.event = Event.CLIENT_REQUEST; if (self.execute()) self.voterFn.handle(loop, item, self.voterArg); else { // Destroy waiting message, no-one to read it ZMsg msg = ZMsg.recvMsg(item.getSocket()); msg.destroy(); } return 0; }; // .until // .split constructor // This is the constructor for our {{bstar}} class. We have to tell it // whether we're primary or backup server, as well as our local and // remote endpoints to bind and connect to: public bstar(boolean primary, String local, String remote) { // Initialize the Binary Star ctx = new ZContext(); loop = new ZLoop(ctx); state = primary ? State.STATE_PRIMARY : State.STATE_BACKUP; // Create publisher for state going to peer statepub = ctx.createSocket(SocketType.PUB); statepub.bind(local); // Create subscriber for state coming from peer // State subscriber Socket statesub = ctx.createSocket(SocketType.SUB); statesub.subscribe(ZMQ.SUBSCRIPTION_ALL); statesub.connect(remote); // Set-up basic reactor events loop.addTimer(BSTAR_HEARTBEAT, 0, SendState, this); PollItem poller = new PollItem(statesub, ZMQ.Poller.POLLIN); loop.addPoller(poller, RecvState, this); } // .split destructor // The destructor shuts down the bstar reactor: public void destroy() { loop.destroy(); ctx.destroy(); } // .split zloop method // This method returns the underlying zloop reactor, so we can add // additional timers and readers: public ZLoop zloop() { return loop; } // .split voter method // This method registers a client voter socket. Messages received // on this socket provide the CLIENT_REQUEST events for the Binary Star // FSM and are passed to the provided application handler. We require // exactly one voter per {{bstar}} instance: public int voter(String endpoint, SocketType type, IZLoopHandler handler, Object arg) { // Hold actual handler+arg so we can call this later Socket socket = ctx.createSocket(type); socket.bind(endpoint); voterFn = handler; voterArg = arg; PollItem poller = new PollItem(socket, ZMQ.Poller.POLLIN); return loop.addPoller(poller, VoterReady, this); } // .split register state-change handlers // Register handlers to be called each time there's a state change: public void newActive(IZLoopHandler handler, Object arg) { activeFn = handler; activeArg = arg; } public void newPassive(IZLoopHandler handler, Object arg) { passiveFn = handler; passiveArg = arg; } // .split enable/disable tracing // Enable/disable verbose tracing, for debugging: public void setVerbose(boolean verbose) { loop.verbose(verbose); } // .split start the reactor // Finally, start the configured reactor. It will end if any handler // returns -1 to the reactor, or if the process receives Interrupt public int start() { assert (voterFn != null); updatePeerExpiry(); return loop.start(); } } jeromq-0.6.0/src/test/java/guide/bstarcli.java000066400000000000000000000066701455771126300212700ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // Binary Star client proof-of-concept implementation. This client does no // real work; it just demonstrates the Binary Star failover model. public class bstarcli { private static final long REQUEST_TIMEOUT = 1000; // msecs private static final long SETTLE_DELAY = 2000; // Before failing over public static void main(String[] argv) throws Exception { try (ZContext ctx = new ZContext()) { String[] server = { "tcp://localhost:5001", "tcp://localhost:5002" }; int serverNbr = 0; System.out.printf("I: connecting to server at %s...\n", server[serverNbr]); Socket client = ctx.createSocket(SocketType.REQ); client.connect(server[serverNbr]); Poller poller = ctx.createPoller(1); poller.register(client, ZMQ.Poller.POLLIN); int sequence = 0; while (!Thread.currentThread().isInterrupted()) { // We send a request, then we work to get a reply String request = String.format("%d", ++sequence); client.send(request); boolean expectReply = true; while (expectReply) { // Poll socket for a reply, with timeout int rc = poller.poll(REQUEST_TIMEOUT); if (rc == -1) break; // Interrupted // .split main body of client // We use a Lazy Pirate strategy in the client. If there's // no reply within our timeout, we close the socket and try // again. In Binary Star, it's the client vote that // decides which server is primary; the client must // therefore try to connect to each server in turn: if (poller.pollin(0)) { // We got a reply from the server, must match getSequence String reply = client.recvStr(); if (Integer.parseInt(reply) == sequence) { System.out.printf("I: server replied OK (%s)\n", reply); expectReply = false; Thread.sleep(1000); // One request per second } else System.out.printf("E: bad reply from server: %s\n", reply); } else { System.out.print("W: no response from server, failing over\n"); // Old socket is confused; close it and open a new one poller.unregister(client); client.close(); serverNbr = (serverNbr + 1) % 2; Thread.sleep(SETTLE_DELAY); System.out.printf("I: connecting to server at %s...\n", server[serverNbr]); client = ctx.createSocket(SocketType.REQ); client.connect(server[serverNbr]); poller.register(client, ZMQ.Poller.POLLIN); // Send request again, on new socket client.send(request); } } } } } } jeromq-0.6.0/src/test/java/guide/bstarsrv.java000066400000000000000000000210111455771126300213150ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; // Binary Star server proof-of-concept implementation. This server does no // real work; it just demonstrates the Binary Star failover model. public class bstarsrv { // States we can be in at any point in time enum State { STATE_PRIMARY, // Primary, waiting for peer to connect STATE_BACKUP, // Backup, waiting for peer to connect STATE_ACTIVE, // Active - accepting connections STATE_PASSIVE // Passive - not accepting connections } // Events, which start with the states our peer can be in enum Event { PEER_PRIMARY, // HA peer is pending primary PEER_BACKUP, // HA peer is pending backup PEER_ACTIVE, // HA peer is active PEER_PASSIVE, // HA peer is passive CLIENT_REQUEST // Client makes request } // Our finite state machine private State state; // Current state private Event event; // Current event private long peerExpiry; // When peer is considered 'dead' // We send state information this often // If peer doesn't respond in two heartbeats, it is 'dead' private final static long HEARTBEAT = 1000; // In msecs // .split Binary Star state machine // The heart of the Binary Star design is its finite-state machine (FSM). // The FSM runs one event at a time. We apply an event to the current state, // which checks if the event is accepted, and if so, sets a new state: private boolean stateMachine() { boolean exception = false; // These are the PRIMARY and BACKUP states; we're waiting to become // ACTIVE or PASSIVE depending on events we get from our peer: if (state == State.STATE_PRIMARY) { if (event == Event.PEER_BACKUP) { System.out.print("I: connected to backup (passive), ready active\n"); state = State.STATE_ACTIVE; } else if (event == Event.PEER_ACTIVE) { System.out.print("I: connected to backup (active), ready passive\n"); state = State.STATE_PASSIVE; } // Accept client connections } else if (state == State.STATE_BACKUP) { if (event == Event.PEER_ACTIVE) { System.out.print("I: connected to primary (active), ready passive\n"); state = State.STATE_PASSIVE; } else // Reject client connections when acting as backup if (event == Event.CLIENT_REQUEST) exception = true; } else // .split active and passive states // These are the ACTIVE and PASSIVE states: if (state == State.STATE_ACTIVE) { if (event == Event.PEER_ACTIVE) { // Two actives would mean split-brain System.out.print("E: fatal error - dual actives, aborting\n"); exception = true; } } else // Server is passive // CLIENT_REQUEST events can trigger failover if peer looks dead if (state == State.STATE_PASSIVE) { if (event == Event.PEER_PRIMARY) { // Peer is restarting - become active, peer will go passive System.out.print("I: primary (passive) is restarting, ready active\n"); state = State.STATE_ACTIVE; } else if (event == Event.PEER_BACKUP) { // Peer is restarting - become active, peer will go passive System.out.print("I: backup (passive) is restarting, ready active\n"); state = State.STATE_ACTIVE; } else if (event == Event.PEER_PASSIVE) { // Two passives would mean cluster would be non-responsive System.out.print("E: fatal error - dual passives, aborting\n"); exception = true; } else if (event == Event.CLIENT_REQUEST) { // Peer becomes active if timeout has passed // It's the client request that triggers the failover assert (peerExpiry > 0); if (System.currentTimeMillis() >= peerExpiry) { // If peer is dead, switch to the active state System.out.print("I: failover successful, ready active\n"); state = State.STATE_ACTIVE; } else // If peer is alive, reject connections exception = true; } } return exception; } // .split main task // This is our main task. First we bind/connect our sockets with our // peer and make sure we will get state messages correctly. We use // three sockets; one to publish state, one to subscribe to state, and // one for client requests/replies: public static void main(String[] argv) { // Arguments can be either of: // -p primary server, at tcp://localhost:5001 // -b backup server, at tcp://localhost:5002 try (ZContext ctx = new ZContext()) { Socket statepub = ctx.createSocket(SocketType.PUB); Socket statesub = ctx.createSocket(SocketType.SUB); statesub.subscribe(ZMQ.SUBSCRIPTION_ALL); Socket frontend = ctx.createSocket(SocketType.ROUTER); bstarsrv fsm = new bstarsrv(); if (argv.length == 1 && argv[0].equals("-p")) { System.out.print("I: Primary active, waiting for backup (passive)\n"); frontend.bind("tcp://*:5001"); statepub.bind("tcp://*:5003"); statesub.connect("tcp://localhost:5004"); fsm.state = State.STATE_PRIMARY; } else if (argv.length == 1 && argv[0].equals("-b")) { System.out.print("I: Backup passive, waiting for primary (active)\n"); frontend.bind("tcp://*:5002"); statepub.bind("tcp://*:5004"); statesub.connect("tcp://localhost:5003"); fsm.state = State.STATE_BACKUP; } else { System.out.print("Usage: bstarsrv { -p | -b }\n"); ctx.destroy(); System.exit(0); } // .split handling socket input // We now process events on our two input sockets, and process // these events one at a time via our finite-state machine. Our // "work" for a client request is simply to echo it back. Poller poller = ctx.createPoller(2); poller.register(frontend, ZMQ.Poller.POLLIN); poller.register(statesub, ZMQ.Poller.POLLIN); // Set timer for next outgoing state message long sendStateAt = System.currentTimeMillis() + HEARTBEAT; while (!Thread.currentThread().isInterrupted()) { int timeLeft = (int) ((sendStateAt - System.currentTimeMillis())); if (timeLeft < 0) timeLeft = 0; int rc = poller.poll(timeLeft); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) { // Have a client request ZMsg msg = ZMsg.recvMsg(frontend); fsm.event = Event.CLIENT_REQUEST; if (!fsm.stateMachine()) // Answer client by echoing request back msg.send(frontend); else msg.destroy(); } if (poller.pollin(1)) { // Have state from our peer, execute as event String message = statesub.recvStr(); fsm.event = Event.values()[Integer.parseInt(message)]; if (fsm.stateMachine()) break; // Error, so exit fsm.peerExpiry = System.currentTimeMillis() + 2 * HEARTBEAT; } // If we timed out, send state to peer if (System.currentTimeMillis() >= sendStateAt) { statepub.send(String.valueOf(fsm.state.ordinal())); sendStateAt = System.currentTimeMillis() + HEARTBEAT; } } if (Thread.currentThread().isInterrupted()) System.out.print("W: interrupted\n"); } } } jeromq-0.6.0/src/test/java/guide/bstarsrv2.java000066400000000000000000000025071455771126300214100ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZLoop.IZLoopHandler; import org.zeromq.ZMsg; // Binary Star server, using bstar reactor public class bstarsrv2 { private static final IZLoopHandler Echo = (loop, item, arg) -> { ZMsg msg = ZMsg.recvMsg(item.getSocket()); msg.send(item.getSocket()); return 0; }; public static void main(String[] argv) { // Arguments can be either of: // -p primary server, at tcp://localhost:5001 // -b backup server, at tcp://localhost:5002 bstar bs = null; if (argv.length == 1 && argv[0].equals("-p")) { System.out.print("I: Primary active, waiting for backup (passive)\n"); bs = new bstar(true, "tcp://*:5003", "tcp://localhost:5004"); bs.voter("tcp://*:5001", SocketType.ROUTER, Echo, null); } else if (argv.length == 1 && argv[0].equals("-b")) { System.out.print("I: Backup passive, waiting for primary (active)\n"); bs = new bstar(false, "tcp://*:5004", "tcp://localhost:5003"); bs.voter("tcp://*:5002", SocketType.ROUTER, Echo, null); } else { System.out.print("Usage: bstarsrv { -p | -b }\n"); System.exit(0); } bs.start(); bs.destroy(); } } jeromq-0.6.0/src/test/java/guide/clone.java000066400000000000000000000307501455771126300205610ustar00rootroot00000000000000package guide; import java.util.HashMap; import java.util.Map; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread.IAttachedRunnable; public class clone { private final ZContext ctx; // Our context wrapper private final Socket pipe; // Pipe through to clone agent // .split constructor and destructor // Here are the constructor and destructor for the clone class. Note that // we create a context specifically for the pipe that connects our // frontend to the backend agent: public clone() { ctx = new ZContext(); pipe = ZThread.fork(ctx, new CloneAgent()); } public void destroy() { ctx.destroy(); } // .split subtree method // Specify subtree for snapshot and updates, which we must do before // connecting to a server as the subtree specification is sent as the // first command to the server. Sends a [SUBTREE][subtree] command to // the agent: public void subtree(String subtree) { ZMsg msg = new ZMsg(); msg.add("SUBTREE"); msg.add(subtree); msg.send(pipe); } // .split connect method // Connect to a new server endpoint. We can connect to at most two // servers. Sends [CONNECT][endpoint][service] to the agent: public void connect(String address, String service) { ZMsg msg = new ZMsg(); msg.add("CONNECT"); msg.add(address); msg.add(service); msg.send(pipe); } // .split set method // Set a new value in the shared hashmap. Sends a [SET][key][value][ttl] // command through to the agent which does the actual work: public void set(String key, String value, int ttl) { ZMsg msg = new ZMsg(); msg.add("SET"); msg.add(key); msg.add(value); msg.add(String.format("%d", ttl)); msg.send(pipe); } // .split get method // Look up value in distributed hash table. Sends [GET][key] to the agent and // waits for a value response. If there is no value available, will eventually // return NULL: public String get(String key) { ZMsg msg = new ZMsg(); msg.add("GET"); msg.add(key); msg.send(pipe); ZMsg reply = ZMsg.recvMsg(pipe); if (reply != null) { String value = reply.popString(); reply.destroy(); return value; } return null; } // .split working with servers // The backend agent manages a set of servers, which we implement using // our simple class model: private static class Server { private final String address; // Server address private final int port; // Server port private final Socket snapshot; // Snapshot socket private final Socket subscriber; // Incoming updates private long expiry; // When server expires private int requests; // How many snapshot requests made? protected Server(ZContext ctx, String address, int port, String subtree) { System.out.printf("I: adding server %s:%d...\n", address, port); this.address = address; this.port = port; snapshot = ctx.createSocket(SocketType.DEALER); snapshot.connect(String.format("%s:%d", address, port)); subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect(String.format("%s:%d", address, port + 1)); subscriber.subscribe(subtree.getBytes(ZMQ.CHARSET)); } protected void destroy() { } } // .split backend agent class // Here is the implementation of the backend agent itself: // Number of servers to which we will talk to private final static int SERVER_MAX = 2; // Server considered dead if silent for this long private final static int SERVER_TTL = 5000; // msecs // States we can be in private final static int STATE_INITIAL = 0; // Before asking server for state private final static int STATE_SYNCING = 1; // Getting state from server private final static int STATE_ACTIVE = 2; // Getting new updates from server private static class Agent { private final ZContext ctx; // Context wrapper private final Socket pipe; // Pipe back to application private final Map kvmap; // Actual key/value table private String subtree; // Subtree specification, if any private final Server[] server; private int nbrServers; // 0 to SERVER_MAX private int state; // Current state private int curServer; // If active, server 0 or 1 private long sequence; // Last kvmsg processed private final Socket publisher; // Outgoing updates protected Agent(ZContext ctx, Socket pipe) { this.ctx = ctx; this.pipe = pipe; kvmap = new HashMap<>(); subtree = ""; state = STATE_INITIAL; publisher = ctx.createSocket(SocketType.PUB); server = new Server[SERVER_MAX]; } protected void destroy() { for (int serverNbr = 0; serverNbr < nbrServers; serverNbr++) server[serverNbr].destroy(); } // .split handling a control message // Here we handle the different control messages from the frontend; // SUBTREE, CONNECT, SET, and GET: private boolean controlMessage() { ZMsg msg = ZMsg.recvMsg(pipe); String command = msg.popString(); if (command == null) return false; // Interrupted if (command.equals("SUBTREE")) { subtree = msg.popString(); } else if (command.equals("CONNECT")) { String address = msg.popString(); String service = msg.popString(); if (nbrServers < SERVER_MAX) { server[nbrServers++] = new Server(ctx, address, Integer.parseInt(service), subtree); // We broadcast updates to all known servers publisher.connect(String.format("%s:%d", address, Integer.parseInt(service) + 2)); } else System.out.printf("E: too many servers (max. %d)\n", SERVER_MAX); } else // .split set and get commands // When we set a property, we push the new key-value pair onto // all our connected servers: if (command.equals("SET")) { String key = msg.popString(); String value = msg.popString(); String ttl = msg.popString(); kvmap.put(key, value); // Send key-value pair on to server kvmsg kvmsg = new kvmsg(0); kvmsg.setKey(key); kvmsg.setUUID(); kvmsg.fmtBody("%s", value); kvmsg.setProp("ttl", ttl); kvmsg.send(publisher); kvmsg.destroy(); } else if (command.equals("GET")) { String key = msg.popString(); String value = kvmap.get(key); if (value != null) pipe.send(value); else pipe.send(""); } msg.destroy(); return true; } } private static class CloneAgent implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { Agent self = new Agent(ctx, pipe); Poller poller = ctx.createPoller(1); poller.register(pipe, Poller.POLLIN); while (!Thread.currentThread().isInterrupted()) { long pollTimer = -1; int pollSize = 2; Server server = self.server[self.curServer]; switch (self.state) { case STATE_INITIAL: // In this state we ask the server for a snapshot, // if we have a server to talk to... if (self.nbrServers > 0) { System.out.printf("I: waiting for server at %s:%d...\n", server.address, server.port); if (server.requests < 2) { server.snapshot.sendMore("ICANHAZ?"); server.snapshot.send(self.subtree); server.requests++; } server.expiry = System.currentTimeMillis() + SERVER_TTL; self.state = STATE_SYNCING; poller.close(); poller = ctx.createPoller(2); poller.register(pipe, Poller.POLLIN); poller.register(server.snapshot, Poller.POLLIN); } else pollSize = 1; break; case STATE_SYNCING: // In this state we read from snapshot and we expect // the server to respond, else we fail over. poller.close(); poller = ctx.createPoller(2); poller.register(pipe, Poller.POLLIN); poller.register(server.snapshot, Poller.POLLIN); break; case STATE_ACTIVE: // In this state we read from subscriber and we expect // the server to give hugz, else we fail over. poller.close(); poller = ctx.createPoller(2); poller.register(pipe, Poller.POLLIN); poller.register(server.subscriber, Poller.POLLIN); break; } if (server != null) { pollTimer = server.expiry - System.currentTimeMillis(); if (pollTimer < 0) pollTimer = 0; } // .split client poll loop // We're ready to process incoming messages; if nothing at all // comes from our server within the timeout, that means the // server is dead: int rc = poller.poll(pollTimer); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) { if (!self.controlMessage()) break; // Interrupted } else if (pollSize == 2 && poller.pollin(1)) { kvmsg msg = kvmsg.recv(poller.getSocket(1)); if (msg == null) break; // Interrupted // Anything from server resets its expiry time server.expiry = System.currentTimeMillis() + SERVER_TTL; if (self.state == STATE_SYNCING) { // Store in snapshot until we're finished server.requests = 0; if (msg.getKey().equals("KTHXBAI")) { self.sequence = msg.getSequence(); self.state = STATE_ACTIVE; System.out.printf("I: received from %s:%d snapshot=%d\n", server.address, server.port, self.sequence); msg.destroy(); } } else if (self.state == STATE_ACTIVE) { // Discard out-of-sequence updates, incl. hugz if (msg.getSequence() > self.sequence) { self.sequence = msg.getSequence(); System.out.printf("I: received from %s:%d update=%d\n", server.address, server.port, self.sequence); } else msg.destroy(); } } else { // Server has died, failover to next System.out.printf("I: server at %s:%d didn't give hugz\n", server.address, server.port); self.curServer = (self.curServer + 1) % self.nbrServers; self.state = STATE_INITIAL; } } self.destroy(); } } } jeromq-0.6.0/src/test/java/guide/clonecli1.java000066400000000000000000000022311455771126300213230ustar00rootroot00000000000000package guide; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; /** * Clone client model 1 * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonecli1 { private static final Map kvMap = new HashMap<>(); private static final AtomicLong sequence = new AtomicLong(); public void run() { try (ZContext ctx = new ZContext()) { Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5556"); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); while (true) { kvsimple kvMsg = kvsimple.recv(subscriber); if (kvMsg == null) break; clonecli1.kvMap.put(kvMsg.getKey(), kvMsg); System.out.println("receiving " + kvMsg); sequence.incrementAndGet(); } } } public static void main(String[] args) { new clonecli1().run(); } } jeromq-0.6.0/src/test/java/guide/clonecli2.java000066400000000000000000000037531455771126300213360ustar00rootroot00000000000000package guide; import java.util.HashMap; import java.util.Map; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Clone client Model Two * * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonecli2 { private static final Map kvMap = new HashMap<>(); public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.DEALER); snapshot.connect("tcp://localhost:5556"); Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5557"); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); // get state snapshot snapshot.send("ICANHAZ?".getBytes(ZMQ.CHARSET), 0); long sequence = 0; while (true) { kvsimple kvMsg = kvsimple.recv(snapshot); if (kvMsg == null) break; sequence = kvMsg.getSequence(); if ("KTHXBAI".equalsIgnoreCase(kvMsg.getKey())) { System.out.println("Received snapshot = " + kvMsg.getSequence()); break; // done } System.out.println("receiving " + kvMsg.getSequence()); clonecli2.kvMap.put(kvMsg.getKey(), kvMsg); } // now apply pending updates, discard out-of-getSequence messages while (true) { kvsimple kvMsg = kvsimple.recv(subscriber); if (kvMsg == null) break; if (kvMsg.getSequence() > sequence) { sequence = kvMsg.getSequence(); System.out.println("receiving " + sequence); clonecli2.kvMap.put(kvMsg.getKey(), kvMsg); } } } } public static void main(String[] args) { new clonecli2().run(); } } jeromq-0.6.0/src/test/java/guide/clonecli3.java000066400000000000000000000063301455771126300213310ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Clone client Model Three * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonecli3 { private static final Map kvMap = new HashMap<>(); public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.DEALER); snapshot.connect("tcp://localhost:5556"); Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5557"); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); Socket push = ctx.createSocket(SocketType.PUSH); push.connect("tcp://localhost:5558"); // get state snapshot long sequence = 0; snapshot.send("ICANHAZ?".getBytes(ZMQ.CHARSET), 0); while (true) { kvsimple kvMsg = kvsimple.recv(snapshot); if (kvMsg == null) break; // Interrupted sequence = kvMsg.getSequence(); if ("KTHXBAI".equalsIgnoreCase(kvMsg.getKey())) { System.out.println( "Received snapshot = " + kvMsg.getSequence() ); break; // done } System.out.println("receiving " + kvMsg.getSequence()); clonecli3.kvMap.put(kvMsg.getKey(), kvMsg); } Poller poller = ctx.createPoller(1); poller.register(subscriber); Random random = new Random(); // now apply pending updates, discard out-of-getSequence messages long alarm = System.currentTimeMillis() + 5000; while (true) { int rc = poller.poll( Math.max(0, alarm - System.currentTimeMillis()) ); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) { kvsimple kvMsg = kvsimple.recv(subscriber); if (kvMsg == null) break; // Interrupted if (kvMsg.getSequence() > sequence) { sequence = kvMsg.getSequence(); System.out.println("receiving " + sequence); clonecli3.kvMap.put(kvMsg.getKey(), kvMsg); } } if (System.currentTimeMillis() >= alarm) { int key = random.nextInt(10000); int body = random.nextInt(1000000); ByteBuffer b = ByteBuffer.allocate(4); b.asIntBuffer().put(body); kvsimple kvUpdateMsg = new kvsimple(String.valueOf(key), 0, b.array()); kvUpdateMsg.send(push); alarm = System.currentTimeMillis() + 1000; } } } } public static void main(String[] args) { new clonecli3().run(); } } jeromq-0.6.0/src/test/java/guide/clonecli4.java000066400000000000000000000066131455771126300213360ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Clone client Model Four * */ public class clonecli4 { // This client is identical to clonecli3 except for where we // handles subtrees. private final static String SUBTREE = "/client/"; private static final Map kvMap = new HashMap<>(); public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.DEALER); snapshot.connect("tcp://localhost:5556"); Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5557"); subscriber.subscribe(SUBTREE.getBytes(ZMQ.CHARSET)); Socket push = ctx.createSocket(SocketType.PUSH); push.connect("tcp://localhost:5558"); // get state snapshot snapshot.sendMore("ICANHAZ?"); snapshot.send(SUBTREE); long sequence = 0; while (true) { kvsimple kvMsg = kvsimple.recv(snapshot); if (kvMsg == null) break; // Interrupted sequence = kvMsg.getSequence(); if ("KTHXBAI".equalsIgnoreCase(kvMsg.getKey())) { System.out.println( "Received snapshot = " + kvMsg.getSequence() ); break; // done } System.out.println("receiving " + kvMsg.getSequence()); clonecli4.kvMap.put(kvMsg.getKey(), kvMsg); } Poller poller = ctx.createPoller(1); poller.register(subscriber); Random random = new Random(); // now apply pending updates, discard out-of-getSequence messages long alarm = System.currentTimeMillis() + 5000; while (true) { int rc = poller.poll( Math.max(0, alarm - System.currentTimeMillis()) ); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) { kvsimple kvMsg = kvsimple.recv(subscriber); if (kvMsg == null) break; // Interrupted if (kvMsg.getSequence() > sequence) { sequence = kvMsg.getSequence(); System.out.println("receiving " + sequence); clonecli4.kvMap.put(kvMsg.getKey(), kvMsg); } } if (System.currentTimeMillis() >= alarm) { String key = String.format( "%s%d", SUBTREE, random.nextInt(10000) ); int body = random.nextInt(1000000); ByteBuffer b = ByteBuffer.allocate(4); b.asIntBuffer().put(body); kvsimple kvUpdateMsg = new kvsimple(key, 0, b.array()); kvUpdateMsg.send(push); alarm = System.currentTimeMillis() + 1000; } } } } public static void main(String[] args) { new clonecli4().run(); } } jeromq-0.6.0/src/test/java/guide/clonecli5.java000066400000000000000000000064541455771126300213420ustar00rootroot00000000000000package guide; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Clone client Model Five * */ public class clonecli5 { // This client is identical to clonecli3 except for where we // handles subtrees. private final static String SUBTREE = "/client/"; public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.DEALER); snapshot.connect("tcp://localhost:5556"); Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5557"); subscriber.subscribe(SUBTREE.getBytes(ZMQ.CHARSET)); Socket publisher = ctx.createSocket(SocketType.PUSH); publisher.connect("tcp://localhost:5558"); Map kvMap = new HashMap<>(); // get state snapshot snapshot.sendMore("ICANHAZ?"); snapshot.send(SUBTREE); long sequence = 0; while (true) { kvmsg kvMsg = kvmsg.recv(snapshot); if (kvMsg == null) break; // Interrupted sequence = kvMsg.getSequence(); if ("KTHXBAI".equalsIgnoreCase(kvMsg.getKey())) { System.out.println( "Received snapshot = " + kvMsg.getSequence() ); kvMsg.destroy(); break; // done } System.out.println("receiving " + kvMsg.getSequence()); kvMsg.store(kvMap); } Poller poller = ctx.createPoller(1); poller.register(subscriber); Random random = new Random(); // now apply pending updates, discard out-of-getSequence messages long alarm = System.currentTimeMillis() + 5000; while (true) { int rc = poller.poll( Math.max(0, alarm - System.currentTimeMillis()) ); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) { kvmsg kvMsg = kvmsg.recv(subscriber); if (kvMsg == null) break; // Interrupted if (kvMsg.getSequence() > sequence) { sequence = kvMsg.getSequence(); System.out.println("receiving " + sequence); kvMsg.store(kvMap); } else kvMsg.destroy(); } if (System.currentTimeMillis() >= alarm) { kvmsg kvMsg = new kvmsg(0); kvMsg.fmtKey("%s%d", SUBTREE, random.nextInt(10000)); kvMsg.fmtBody("%d", random.nextInt(1000000)); kvMsg.setProp("ttl", "%d", random.nextInt(30)); kvMsg.send(publisher); kvMsg.destroy(); alarm = System.currentTimeMillis() + 1000; } } } } public static void main(String[] args) { new clonecli5().run(); } } jeromq-0.6.0/src/test/java/guide/clonecli6.java000066400000000000000000000021271455771126300213340ustar00rootroot00000000000000package guide; import java.util.Random; /** * Clone client model 6 */ public class clonecli6 { private final static String SUBTREE = "/client/"; public void run() { // Create distributed hash instance clone clone = new clone(); Random rand = new Random(System.nanoTime()); // Specify configuration clone.subtree(SUBTREE); clone.connect("tcp://localhost", "5556"); clone.connect("tcp://localhost", "5566"); // Set random tuples into the distributed hash while (!Thread.currentThread().isInterrupted()) { // Set random value, check it was stored String key = String.format("%s%d", SUBTREE, rand.nextInt(10000)); String value = String.format("%d", rand.nextInt(1000000)); clone.set(key, value, rand.nextInt(30)); try { Thread.sleep(1000); } catch (InterruptedException e) { } } clone.destroy(); } public static void main(String[] args) { new clonecli6().run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv1.java000066400000000000000000000026121455771126300213710ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * * Clone server model 1 * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonesrv1 { private static final AtomicLong sequence = new AtomicLong(); public void run() { try (ZContext ctx = new ZContext()) { Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:5556"); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } Random random = new Random(); while (true) { long currentSequenceNumber = sequence.incrementAndGet(); int key = random.nextInt(10000); int body = random.nextInt(1000000); ByteBuffer b = ByteBuffer.allocate(4); b.asIntBuffer().put(body); kvsimple kvMsg = new kvsimple(String.valueOf(key), currentSequenceNumber, b.array() ); kvMsg.send(publisher); System.out.println("sending " + kvMsg); } } } public static void main(String[] args) { new clonesrv1().run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv2.java000066400000000000000000000102171455771126300213720ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread; import org.zeromq.ZThread.IAttachedRunnable; /** * Clone server Model Two * * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonesrv2 { public void run() { try (ZContext ctx = new ZContext()) { Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:5557"); Socket updates = ZThread.fork(ctx, new StateManager()); Random random = new Random(); long sequence = 0; while (!Thread.currentThread().isInterrupted()) { long currentSequenceNumber = ++sequence; int key = random.nextInt(10000); int body = random.nextInt(1000000); ByteBuffer b = ByteBuffer.allocate(4); b.asIntBuffer().put(body); kvsimple kvMsg = new kvsimple(String.valueOf(key), currentSequenceNumber, b.array() ); kvMsg.send(publisher); kvMsg.send(updates); // send a message to State Manager thread. try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.printf(" Interrupted\n%d messages out\n", sequence); } } public static class StateManager implements IAttachedRunnable { private static final Map kvMap = new LinkedHashMap<>(); @Override public void run(Object[] args, ZContext ctx, Socket pipe) { pipe.send("READY"); // optional Socket snapshot = ctx.createSocket(SocketType.ROUTER); snapshot.bind("tcp://*:5556"); Poller poller = ctx.createPoller(2); poller.register(pipe, ZMQ.Poller.POLLIN); poller.register(snapshot, ZMQ.Poller.POLLIN); long stateSequence = 0; while (!Thread.currentThread().isInterrupted()) { if (poller.poll() < 0) break; // Context has been shut down // apply state updates from main thread if (poller.pollin(0)) { kvsimple kvMsg = kvsimple.recv(pipe); if (kvMsg == null) break; StateManager.kvMap.put(kvMsg.getKey(), kvMsg); stateSequence = kvMsg.getSequence(); } // execute state snapshot request if (poller.pollin(1)) { byte[] identity = snapshot.recv(0); if (identity == null) break; String request = new String(snapshot.recv(0), ZMQ.CHARSET); if (!request.equals("ICANHAZ?")) { System.out.println("E: bad request, aborting"); break; } for (Entry entry : kvMap.entrySet()) { kvsimple msg = entry.getValue(); System.out.println("Sending message " + entry.getValue().getSequence()); this.sendMessage(msg, identity, snapshot); } // now send end message with getSequence number System.out.println("Sending state snapshot = " + stateSequence); snapshot.send(identity, ZMQ.SNDMORE); kvsimple message = new kvsimple("KTHXBAI", stateSequence, ZMQ.MESSAGE_SEPARATOR); message.send(snapshot); } } } private void sendMessage(kvsimple msg, byte[] identity, Socket snapshot) { snapshot.send(identity, ZMQ.SNDMORE); msg.send(snapshot); } } public static void main(String[] args) { new clonesrv2().run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv3.java000066400000000000000000000063071455771126300214000ustar00rootroot00000000000000package guide; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Clone server Model Three * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class clonesrv3 { private static final Map kvMap = new LinkedHashMap<>(); public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.ROUTER); snapshot.bind("tcp://*:5556"); Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:5557"); Socket collector = ctx.createSocket(SocketType.PULL); collector.bind("tcp://*:5558"); Poller poller = ctx.createPoller(2); poller.register(collector, Poller.POLLIN); poller.register(snapshot, Poller.POLLIN); long sequence = 0; while (!Thread.currentThread().isInterrupted()) { if (poller.poll(1000) < 0) break; // Context has been shut down // apply state updates from main thread if (poller.pollin(0)) { kvsimple kvMsg = kvsimple.recv(collector); if (kvMsg == null) // Interrupted break; kvMsg.setSequence(++sequence); kvMsg.send(publisher); clonesrv3.kvMap.put(kvMsg.getKey(), kvMsg); System.out.printf("I: publishing update %5d\n", sequence); } // execute state snapshot request if (poller.pollin(1)) { byte[] identity = snapshot.recv(0); if (identity == null) break; // Interrupted String request = snapshot.recvStr(); if (!request.equals("ICANHAZ?")) { System.out.println("E: bad request, aborting"); break; } for (Entry entry : kvMap.entrySet()) { kvsimple msg = entry.getValue(); System.out.println("Sending message " + entry.getValue().getSequence()); this.sendMessage(msg, identity, snapshot); } // now send end message with getSequence number System.out.println("Sending state snapshot = " + sequence); snapshot.send(identity, ZMQ.SNDMORE); kvsimple message = new kvsimple( "KTHXBAI", sequence, ZMQ.SUBSCRIPTION_ALL ); message.send(snapshot); } } System.out.printf(" Interrupted\n%d messages handled\n", sequence); } } private void sendMessage(kvsimple msg, byte[] identity, Socket snapshot) { snapshot.send(identity, ZMQ.SNDMORE); msg.send(snapshot); } public static void main(String[] args) { new clonesrv3().run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv4.java000066400000000000000000000065501455771126300214010ustar00rootroot00000000000000package guide; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Clone server Model Four */ public class clonesrv4 { private static final Map kvMap = new LinkedHashMap<>(); public void run() { try (ZContext ctx = new ZContext()) { Socket snapshot = ctx.createSocket(SocketType.ROUTER); snapshot.bind("tcp://*:5556"); Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:5557"); Socket collector = ctx.createSocket(SocketType.PULL); collector.bind("tcp://*:5558"); Poller poller = ctx.createPoller(2); poller.register(collector, Poller.POLLIN); poller.register(snapshot, Poller.POLLIN); long sequence = 0; while (!Thread.currentThread().isInterrupted()) { if (poller.poll(1000) < 0) break; // Context has been shut down // apply state updates from main thread if (poller.pollin(0)) { kvsimple kvMsg = kvsimple.recv(collector); if (kvMsg == null) // Interrupted break; kvMsg.setSequence(++sequence); kvMsg.send(publisher); clonesrv4.kvMap.put(kvMsg.getKey(), kvMsg); System.out.printf("I: publishing update %5d\n", sequence); } // execute state snapshot request if (poller.pollin(1)) { byte[] identity = snapshot.recv(0); if (identity == null) break; // Interrupted // .until // Request is in second frame of message String request = snapshot.recvStr(); if (!request.equals("ICANHAZ?")) { System.out.println("E: bad request, aborting"); break; } String subtree = snapshot.recvStr(); for (Entry entry : kvMap.entrySet()) { kvsimple msg = entry.getValue(); System.out.println("Sending message " + entry.getValue().getSequence()); this.sendMessage(msg, identity, subtree, snapshot); } // now send end message with getSequence number System.out.println("Sending state snapshot = " + sequence); snapshot.send(identity, ZMQ.SNDMORE); kvsimple message = new kvsimple( "KTHXBAI", sequence, ZMQ.SUBSCRIPTION_ALL ); message.send(snapshot); } } System.out.printf(" Interrupted\n%d messages handled\n", sequence); } } private void sendMessage(kvsimple msg, byte[] identity, String subtree, Socket snapshot) { snapshot.send(identity, ZMQ.SNDMORE); snapshot.send(subtree, ZMQ.SNDMORE); msg.send(snapshot); } public static void main(String[] args) { new clonesrv4().run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv5.java000066400000000000000000000140161455771126300213760ustar00rootroot00000000000000package guide; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZLoop; import org.zeromq.ZLoop.IZLoopHandler; import org.zeromq.ZMQ; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Socket; // Clone server - Model Five public class clonesrv5 { private final ZContext ctx; // Context wrapper private final Map kvmap; // Key-value store private final ZLoop loop; // zloop reactor private long sequence; // How many updates we're at private final Socket snapshot; // Handle snapshot requests private final Socket publisher; // Publish updates to clients private final Socket collector; // Collect updates from clients // .split snapshot handler // This is the reactor handler for the snapshot socket; it accepts // just the ICANHAZ? request and replies with a state snapshot ending // with a KTHXBAI message: private static class Snapshots implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv5 srv = (clonesrv5) arg; Socket socket = item.getSocket(); byte[] identity = socket.recv(); if (identity != null) { // Request is in second frame of message String request = socket.recvStr(); String subtree = null; if (request.equals("ICANHAZ?")) { subtree = socket.recvStr(); } else System.out.print("E: bad request, aborting\n"); if (subtree != null) { // Send state socket to client for (Entry entry : srv.kvmap.entrySet()) { sendSingle(entry.getValue(), identity, subtree, socket); } // Now send END message with getSequence number System.out.printf("I: sending shapshot=%d\n", srv.sequence); socket.send(identity, ZMQ.SNDMORE); kvmsg kvmsg = new kvmsg(srv.sequence); kvmsg.setKey("KTHXBAI"); kvmsg.setBody(subtree.getBytes(ZMQ.CHARSET)); kvmsg.send(socket); kvmsg.destroy(); } } return 0; } } // .split collect updates // We store each update with a new getSequence number, and if necessary, a // time-to-live. We publish updates immediately on our publisher socket: private static class Collector implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv5 srv = (clonesrv5) arg; Socket socket = item.getSocket(); kvmsg msg = kvmsg.recv(socket); if (msg != null) { msg.setSequence(++srv.sequence); msg.send(srv.publisher); int ttl = Integer.parseInt(msg.getProp("ttl")); if (ttl > 0) msg.setProp("ttl", "%d", System.currentTimeMillis() + ttl * 1000); msg.store(srv.kvmap); System.out.printf("I: publishing update=%d\n", srv.sequence); } return 0; } } private static class FlushTTL implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv5 srv = (clonesrv5) arg; if (srv.kvmap != null) { for (kvmsg msg : new ArrayList<>(srv.kvmap.values())) { srv.flushSingle(msg); } } return 0; } } public clonesrv5() { // Main port we're working on int port = 5556; ctx = new ZContext(); kvmap = new HashMap<>(); loop = new ZLoop(ctx); loop.verbose(false); // Set up our clone server sockets snapshot = ctx.createSocket(SocketType.ROUTER); snapshot.bind(String.format("tcp://*:%d", port)); publisher = ctx.createSocket(SocketType.PUB); publisher.bind(String.format("tcp://*:%d", port + 1)); collector = ctx.createSocket(SocketType.PULL); collector.bind(String.format("tcp://*:%d", port + 2)); } public void run() { // Register our handlers with reactor PollItem poller = new PollItem(snapshot, ZMQ.Poller.POLLIN); loop.addPoller(poller, new Snapshots(), this); poller = new PollItem(collector, ZMQ.Poller.POLLIN); loop.addPoller(poller, new Collector(), this); loop.addTimer(1000, 0, new FlushTTL(), this); loop.start(); loop.destroy(); ctx.destroy(); } // We call this function for each getKey-value pair in our hash table private static void sendSingle(kvmsg msg, byte[] identity, String subtree, Socket socket) { if (msg.getKey().startsWith(subtree)) { socket.send(identity, // Choose recipient ZMQ.SNDMORE); msg.send(socket); } } // .split flush ephemeral values // At regular intervals, we flush ephemeral values that have expired. This // could be slow on very large data sets: // If getKey-value pair has expired, delete it and publish the // fact to listening clients. private void flushSingle(kvmsg msg) { long ttl = Long.parseLong(msg.getProp("ttl")); if (ttl > 0 && System.currentTimeMillis() >= ttl) { msg.setSequence(++sequence); msg.setBody(ZMQ.MESSAGE_SEPARATOR); msg.send(publisher); msg.store(kvmap); System.out.printf("I: publishing delete=%d\n", sequence); } } public static void main(String[] args) { clonesrv5 srv = new clonesrv5(); srv.run(); } } jeromq-0.6.0/src/test/java/guide/clonesrv6.java000066400000000000000000000341011455771126300213740ustar00rootroot00000000000000package guide; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZLoop; import org.zeromq.ZLoop.IZLoopHandler; import org.zeromq.ZMQ; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Socket; // Clone server - Model Six public class clonesrv6 { private final ZContext ctx; // Context wrapper private Map kvmap; // Key-value store private final bstar bStar; // Bstar reactor core private long sequence; // How many updates we're at private final int peer; // Main port of our peer private final Socket publisher; // Publish updates and hugz private final Socket collector; // Collect updates from clients private final Socket subscriber; // Get updates from peer private final List pending; // Pending updates from clients private boolean active; // TRUE if we're active private boolean passive; // TRUE if we're passive private static class Snapshots implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; Socket socket = item.getSocket(); byte[] identity = socket.recv(); if (identity != null) { // Request is in second frame of message String request = socket.recvStr(); String subtree = null; if (request.equals("ICANHAZ?")) { subtree = socket.recvStr(); } else System.out.print("E: bad request, aborting\n"); if (subtree != null) { // Send state socket to client for (Entry entry : srv.kvmap.entrySet()) { sendSingle(entry.getValue(), identity, subtree, socket); } // Now send END message with getSequence number System.out.printf("I: sending shapshot=%d\n", srv.sequence); socket.send(identity, ZMQ.SNDMORE); kvmsg kvmsg = new kvmsg(srv.sequence); kvmsg.setKey("KTHXBAI"); kvmsg.setBody(subtree.getBytes(ZMQ.CHARSET)); kvmsg.send(socket); kvmsg.destroy(); } } return 0; } } private static class Collector implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; Socket socket = item.getSocket(); kvmsg msg = kvmsg.recv(socket); if (msg != null) { if (srv.active) { msg.setSequence(++srv.sequence); msg.send(srv.publisher); int ttl = Integer.parseInt(msg.getProp("ttl")); if (ttl > 0) msg.setProp("ttl", "%d", System.currentTimeMillis() + ttl * 1000); msg.store(srv.kvmap); System.out.printf("I: publishing update=%d\n", srv.sequence); } else { // If we already got message from active, drop it, else // hold on pending list if (srv.wasPending(msg)) msg.destroy(); else srv.pending.add(msg); } } return 0; } } // .split heartbeating // We send a HUGZ message once a second to all subscribers so that they // can detect if our server dies. They'll then switch over to the backup // server, which will become active: private static class SendHugz implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; kvmsg msg = new kvmsg(srv.sequence); msg.setKey("HUGZ"); msg.setBody(ZMQ.MESSAGE_SEPARATOR); msg.send(srv.publisher); msg.destroy(); return 0; } } private static class FlushTTL implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; if (srv.kvmap != null) { for (kvmsg msg : new ArrayList<>(srv.kvmap.values())) { srv.flushSingle(msg); } } return 0; } } // .split handling state changes // When we switch from passive to active, we apply our pending list so that // our kvmap is up-to-date. When we switch to passive, we wipe our kvmap // and grab a new snapshot from the active server: private static class NewActive implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; srv.active = true; srv.passive = false; // Stop subscribing to updates PollItem poller = new PollItem(srv.subscriber, ZMQ.Poller.POLLIN); srv.bStar.zloop().removePoller(poller); // Apply pending list to own hash table for (kvmsg msg : srv.pending) { msg.setSequence(++srv.sequence); msg.send(srv.publisher); msg.store(srv.kvmap); System.out.printf("I: publishing pending=%d\n", srv.sequence); } return 0; } } private static class NewPassive implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; if (srv.kvmap != null) { for (kvmsg msg : srv.kvmap.values()) msg.destroy(); } srv.active = false; srv.passive = true; // Start subscribing to updates PollItem poller = new PollItem(srv.subscriber, ZMQ.Poller.POLLIN); srv.bStar.zloop().addPoller(poller, new Subscriber(), srv); return 0; } } // .split subscriber handler // When we get an update, we create a new kvmap if necessary, and then // add our update to our kvmap. We're always passive in this case: private static class Subscriber implements IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg) { clonesrv6 srv = (clonesrv6) arg; Socket socket = item.getSocket(); // Get state snapshot if necessary if (srv.kvmap == null) { srv.kvmap = new HashMap<>(); Socket snapshot = srv.ctx.createSocket(SocketType.DEALER); snapshot.connect(String.format("tcp://localhost:%d", srv.peer)); System.out.printf("I: asking for snapshot from: tcp://localhost:%d\n", srv.peer); snapshot.sendMore("ICANHAZ?"); snapshot.send(""); // blank subtree to get all while (true) { kvmsg msg = kvmsg.recv(snapshot); if (msg == null) break; // Interrupted if (msg.getKey().equals("KTHXBAI")) { srv.sequence = msg.getSequence(); msg.destroy(); break; // Done } msg.store(srv.kvmap); } System.out.printf("I: received snapshot=%d\n", srv.sequence); snapshot.close(); } // Find and remove update off pending list kvmsg msg = kvmsg.recv(item.getSocket()); if (msg == null) return 0; if (!msg.getKey().equals("HUGZ")) { if (!srv.wasPending(msg)) { // If active update came before client update, flip it // around, store active update (with sequence) on pending // list and use to clear client update when it comes later srv.pending.add(msg.dup()); } // If update is more recent than our kvmap, apply it if (msg.getSequence() > srv.sequence) { srv.sequence = msg.getSequence(); msg.store(srv.kvmap); System.out.printf("I: received update=%d\n", srv.sequence); } } msg.destroy(); return 0; } } public clonesrv6(boolean primary) { // TRUE if we're primary boolean primary1; // Main port we're working on int port; if (primary) { bStar = new bstar(true, "tcp://*:5003", "tcp://localhost:5004"); bStar.voter("tcp://*:5556", SocketType.ROUTER, new Snapshots(), this); port = 5556; peer = 5566; } else { bStar = new bstar(false, "tcp://*:5004", "tcp://localhost:5003"); bStar.voter("tcp://*:5566", SocketType.ROUTER, new Snapshots(), this); port = 5566; peer = 5556; } // Primary server will become first active if (primary) kvmap = new HashMap<>(); ctx = new ZContext(); pending = new ArrayList<>(); bStar.setVerbose(true); // Set up our clone server sockets publisher = ctx.createSocket(SocketType.PUB); collector = ctx.createSocket(SocketType.SUB); collector.subscribe(ZMQ.SUBSCRIPTION_ALL); publisher.bind(String.format("tcp://*:%d", port + 1)); collector.bind(String.format("tcp://*:%d", port + 2)); // Set up our own clone client interface to peer subscriber = ctx.createSocket(SocketType.SUB); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); subscriber.connect(String.format("tcp://localhost:%d", peer + 1)); } // .split main task body // After we've setup our sockets, we register our binary star // event handlers, and then start the bstar reactor. This finishes // when the user presses Ctrl-C or when the process receives a SIGINT // interrupt: public void run() { // Register state change handlers bStar.newActive(new NewActive(), this); bStar.newPassive(new NewPassive(), this); // Register our other handlers with the bstar reactor PollItem poller = new PollItem(collector, ZMQ.Poller.POLLIN); bStar.zloop().addPoller(poller, new Collector(), this); bStar.zloop().addTimer(1000, 0, new FlushTTL(), this); bStar.zloop().addTimer(1000, 0, new SendHugz(), this); // Start the bstar reactor bStar.start(); // Interrupted, so shut down for (kvmsg value : pending) value.destroy(); bStar.destroy(); for (kvmsg value : kvmap.values()) value.destroy(); ctx.destroy(); } // Send one state snapshot key-value pair to a socket // Hash item data is our kvmsg object, ready to send private static void sendSingle(kvmsg msg, byte[] identity, String subtree, Socket socket) { if (msg.getKey().startsWith(subtree)) { socket.send(identity, // Choose recipient ZMQ.SNDMORE); msg.send(socket); } } // The collector is more complex than in the clonesrv5 example because the // way it processes updates depends on whether we're active or passive. // The active applies them immediately to its kvmap, whereas the passive // queues them as pending: // If message was already on pending list, remove it and return TRUE, // else return FALSE. boolean wasPending(kvmsg msg) { Iterator it = pending.iterator(); while (it.hasNext()) { if (java.util.Arrays.equals(msg.UUID(), it.next().UUID())) { it.remove(); return true; } } return false; } // We purge ephemeral values using exactly the same code as in // the previous clonesrv5 example. // .skip // If key-value pair has expired, delete it and publish the // fact to listening clients. private void flushSingle(kvmsg msg) { long ttl = Long.parseLong(msg.getProp("ttl")); if (ttl > 0 && System.currentTimeMillis() >= ttl) { msg.setSequence(++sequence); msg.setBody(ZMQ.MESSAGE_SEPARATOR); msg.send(publisher); msg.store(kvmap); System.out.printf("I: publishing delete=%d\n", sequence); } } // .split main task setup // The main task parses the command line to decide whether to start // as a primary or backup server. We're using the Binary Star pattern // for reliability. This interconnects the two servers so they can // agree on which one is primary and which one is backup. To allow the // two servers to run on the same box, we use different ports for // primary and backup. Ports 5003/5004 are used to interconnect the // servers. Ports 5556/5566 are used to receive voting events (snapshot // requests in the clone pattern). Ports 5557/5567 are used by the // publisher, and ports 5558/5568 are used by the collector: public static void main(String[] args) { clonesrv6 srv = null; if (args.length == 1 && "-p".equals(args[0])) { srv = new clonesrv6(true); } else if (args.length == 1 && "-b".equals(args[0])) { srv = new clonesrv6(false); } else { System.out.print("Usage: clonesrv4 { -p | -b }\n"); System.exit(0); } srv.run(); } } jeromq-0.6.0/src/test/java/guide/espresso.java000066400000000000000000000071001455771126300213150ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread.IAttachedRunnable; // Espresso Pattern // This shows how to capture data using a pub-sub proxy public class espresso { // The subscriber thread requests messages starting with // A and B, then reads and counts incoming messages. private static class Subscriber implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { // Subscribe to "A" and "B" Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:6001"); subscriber.subscribe("A".getBytes(ZMQ.CHARSET)); subscriber.subscribe("B".getBytes(ZMQ.CHARSET)); int count = 0; while (count < 5) { String string = subscriber.recvStr(); if (string == null) break; // Interrupted count++; } subscriber.close(); } } // .split publisher thread // The publisher sends random messages starting with A-J: private static class Publisher implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:6000"); Random rand = new Random(System.currentTimeMillis()); while (!Thread.currentThread().isInterrupted()) { String string = String.format("%c-%05d", 'A' + rand.nextInt(10), rand.nextInt(100000)); if (!publisher.send(string)) break; // Interrupted try { Thread.sleep(100); // Wait for 1/10th second } catch (InterruptedException e) { } } publisher.close(); } } // .split listener thread // The listener receives all messages flowing through the proxy, on its // pipe. In CZMQ, the pipe is a pair of ZMQ_PAIR sockets that connect // attached child threads. In other languages your mileage may vary: private static class Listener implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { // Print everything that arrives on pipe while (true) { ZFrame frame = ZFrame.recvFrame(pipe); if (frame == null) break; // Interrupted frame.print(null); frame.destroy(); } } } // .split main thread // The main task starts the subscriber and publisher, and then sets // itself up as a listening proxy. The listener runs as a child thread: public static void main(String[] argv) { try (ZContext ctx = new ZContext()) { // Start child threads ZThread.fork(ctx, new Publisher()); ZThread.fork(ctx, new Subscriber()); Socket subscriber = ctx.createSocket(SocketType.XSUB); subscriber.connect("tcp://localhost:6000"); Socket publisher = ctx.createSocket(SocketType.XPUB); publisher.bind("tcp://*:6001"); Socket listener = ZThread.fork(ctx, new Listener()); ZMQ.proxy(subscriber, publisher, listener); System.out.println(" interrupted"); // NB: child threads exit here when the context is closed } } } jeromq-0.6.0/src/test/java/guide/flcliapi.java000066400000000000000000000253201455771126300212410ustar00rootroot00000000000000package guide; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread.IAttachedRunnable; // flcliapi class - Freelance Pattern agent class // Implements the Freelance Protocol at http://rfc.zeromq.org/spec:10 public class flcliapi { // If not a single service replies within this time, give up private static final int GLOBAL_TIMEOUT = 2500; // PING interval for servers we think are alive private static final int PING_INTERVAL = 2000; // msecs // Server considered dead if silent for this long private static final int SERVER_TTL = 6000; // msecs // .split API structure // This API works in two halves, a common pattern for APIs that need to // run in the background. One half is an frontend object our application // creates and works with; the other half is a backend "agent" that runs // in a background thread. The frontend talks to the backend over an // inproc pipe socket: // Structure of our frontend class private final ZContext ctx; // Our context wrapper private final Socket pipe; // Pipe through to flcliapi agent public flcliapi() { ctx = new ZContext(); FreelanceAgent agent = new FreelanceAgent(); pipe = ZThread.fork(ctx, agent); } public void destroy() { ctx.destroy(); } // .split connect method // To implement the connect method, the frontend object sends a multipart // message to the backend agent. The first part is a string "CONNECT", and // the second part is the endpoint. It waits 100msec for the connection to // come up, which isn't pretty, but saves us from sending all requests to a // single server, at startup time: public void connect(String endpoint) { ZMsg msg = new ZMsg(); msg.add("CONNECT"); msg.add(endpoint); msg.send(pipe); try { Thread.sleep(100); // Allow connection to come up } catch (InterruptedException e) { } } // .split request method // To implement the request method, the frontend object sends a message // to the backend, specifying a command "REQUEST" and the request message: public ZMsg request(ZMsg request) { request.push("REQUEST"); request.send(pipe); ZMsg reply = ZMsg.recvMsg(pipe); if (reply != null) { String status = reply.popString(); if (status.equals("FAILED")) reply.destroy(); } return reply; } // .split backend agent // Here we see the backend agent. It runs as an attached thread, talking // to its parent over a pipe socket. It is a fairly complex piece of work // so we'll break it down into pieces. First, the agent manages a set of // servers, using our familiar class approach: // Simple class for one server we talk to private static class Server { private final String endpoint; // Server identity/endpoint private boolean alive; // 1 if known to be alive private long pingAt; // Next ping at this time private long expires; // Expires at this time protected Server(String endpoint) { this.endpoint = endpoint; alive = false; pingAt = System.currentTimeMillis() + PING_INTERVAL; expires = System.currentTimeMillis() + SERVER_TTL; } protected void destroy() { } private void ping(Socket socket) { if (System.currentTimeMillis() >= pingAt) { ZMsg ping = new ZMsg(); ping.add(endpoint); ping.add("PING"); ping.send(socket); pingAt = System.currentTimeMillis() + PING_INTERVAL; } } private long tickless(long tickless) { if (tickless > pingAt) return pingAt; return -1; } } // .split backend agent class // We build the agent as a class that's capable of processing messages // coming in from its various sockets: // Simple class for one background agent private static class Agent { private final Socket pipe; // Socket to talk back to application private final Socket router; // Socket to talk to servers private final Map servers; // Servers we've connected to private final List actives; // Servers we know are alive private int sequence; // Number of requests ever sent private ZMsg request; // Current request if any private ZMsg reply; // Current reply if any private long expires; // Timeout for request/reply protected Agent(ZContext ctx, Socket pipe) { // Own context this.pipe = pipe; router = ctx.createSocket(SocketType.ROUTER); servers = new HashMap<>(); actives = new ArrayList<>(); } protected void destroy() { for (Server server : servers.values()) server.destroy(); } // .split control messages // This method processes one message from our frontend class // (it's going to be CONNECT or REQUEST): // Callback when we remove server from agent 'servers' hash table private void controlMessage() { ZMsg msg = ZMsg.recvMsg(pipe); String command = msg.popString(); if (command.equals("CONNECT")) { String endpoint = msg.popString(); System.out.printf("I: connecting to %s...\n", endpoint); router.connect(endpoint); Server server = new Server(endpoint); servers.put(endpoint, server); actives.add(server); server.pingAt = System.currentTimeMillis() + PING_INTERVAL; server.expires = System.currentTimeMillis() + SERVER_TTL; } else if (command.equals("REQUEST")) { assert (request == null); // Strict request-reply cycle // Prefix request with getSequence number and empty envelope String sequenceText = String.format("%d", ++sequence); msg.push(sequenceText); // Take ownership of request message request = msg; msg = null; // Request expires after global timeout expires = System.currentTimeMillis() + GLOBAL_TIMEOUT; } if (msg != null) msg.destroy(); } // .split router messages // This method processes one message from a connected // server: private void routerMessage() { ZMsg reply = ZMsg.recvMsg(router); // Frame 0 is server that replied String endpoint = reply.popString(); Server server = servers.get(endpoint); assert (server != null); if (!server.alive) { actives.add(server); server.alive = true; } server.pingAt = System.currentTimeMillis() + PING_INTERVAL; server.expires = System.currentTimeMillis() + SERVER_TTL; // Frame 1 may be getSequence number for reply String sequenceStr = reply.popString(); if (Integer.parseInt(sequenceStr) == sequence) { reply.push("OK"); reply.send(pipe); request.destroy(); request = null; } else reply.destroy(); } } // .split backend agent implementation // Finally, here's the agent task itself, which polls its two sockets // and processes incoming messages: static private class FreelanceAgent implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { Agent agent = new Agent(ctx, pipe); Poller poller = ctx.createPoller(2); poller.register(agent.pipe, Poller.POLLIN); poller.register(agent.router, Poller.POLLIN); while (!Thread.currentThread().isInterrupted()) { // Calculate tickless timer, up to 1 hour long tickless = System.currentTimeMillis() + 1000 * 3600; if (agent.request != null && tickless > agent.expires) tickless = agent.expires; for (Server server : agent.servers.values()) { long newTickless = server.tickless(tickless); if (newTickless > 0) tickless = newTickless; } int rc = poller.poll(tickless - System.currentTimeMillis()); if (rc == -1) break; // Context has been shut down if (poller.pollin(0)) agent.controlMessage(); if (poller.pollin(1)) agent.routerMessage(); // If we're processing a request, dispatch to next server if (agent.request != null) { if (System.currentTimeMillis() >= agent.expires) { // Request expired, kill it agent.pipe.send("FAILED"); agent.request.destroy(); agent.request = null; } else { // Find server to talk to, remove any expired ones while (!agent.actives.isEmpty()) { Server server = agent.actives.get(0); if (System.currentTimeMillis() >= server.expires) { agent.actives.remove(0); server.alive = false; } else { ZMsg request = agent.request.duplicate(); request.push(server.endpoint); request.send(agent.router); break; } } } } // Disconnect and delete any expired servers // Send heartbeats to idle servers if needed for (Server server : agent.servers.values()) server.ping(agent.router); } agent.destroy(); } } } jeromq-0.6.0/src/test/java/guide/flclient1.java000066400000000000000000000056101455771126300213370ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; // Freelance client - Model 1 // Uses REQ socket to query one or more services public class flclient1 { private static final int REQUEST_TIMEOUT = 1000; private static final int MAX_RETRIES = 3; // Before we abandon private static ZMsg tryRequest(ZContext ctx, String endpoint, ZMsg request) { System.out.printf("I: trying echo service at %s...\n", endpoint); Socket client = ctx.createSocket(SocketType.REQ); client.connect(endpoint); // Send request, wait safely for reply ZMsg msg = request.duplicate(); msg.send(client); Poller poller = ctx.createPoller(1); poller.register(client, Poller.POLLIN); poller.poll(REQUEST_TIMEOUT); ZMsg reply = null; if (poller.pollin(0)) reply = ZMsg.recvMsg(client); // Close socket in any case, we're done with it now client.close(); poller.close(); return reply; } // .split client task // The client uses a Lazy Pirate strategy if it only has one server to talk // to. If it has two or more servers to talk to, it will try each server just // once: public static void main(String[] argv) { try (ZContext ctx = new ZContext()) { ZMsg request = new ZMsg(); request.add("Hello world"); ZMsg reply = null; int endpoints = argv.length; if (endpoints == 0) System.out.print("I: syntax: flclient1 ...\n"); else if (endpoints == 1) { // For one endpoint, we retry N times int retries; for (retries = 0; retries < MAX_RETRIES; retries++) { String endpoint = argv[0]; reply = tryRequest(ctx, endpoint, request); if (reply != null) break; // Successful System.out.printf( "W: no response from %s, retrying...\n", endpoint ); } } else { // For multiple endpoints, try each at most once int endpointNbr; for (endpointNbr = 0; endpointNbr < endpoints; endpointNbr++) { String endpoint = argv[endpointNbr]; reply = tryRequest(ctx, endpoint, request); if (reply != null) break; // Successful System.out.printf("W: no response from %s\n", endpoint); } } if (reply != null) { System.out.print("Service is running OK\n"); reply.destroy(); } request.destroy(); } } } jeromq-0.6.0/src/test/java/guide/flclient2.java000066400000000000000000000072141455771126300213420ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; // Freelance client - Model 2 // Uses DEALER socket to blast one or more services public class flclient2 { // If not a single service replies within this time, give up private static final int GLOBAL_TIMEOUT = 2500; // .split class implementation // Here is the {{flclient}} class implementation. Each instance has a // context, a DEALER socket it uses to talk to the servers, a counter // of how many servers it's connected to, and a request getSequence number: private final ZContext ctx; // Our context wrapper private final Socket socket; // DEALER socket talking to servers private int servers; // How many servers we have connected to private int sequence; // Number of requests ever sent public flclient2() { ctx = new ZContext(); socket = ctx.createSocket(SocketType.DEALER); } public void destroy() { ctx.destroy(); } private void connect(String endpoint) { socket.connect(endpoint); servers++; } private ZMsg request(ZMsg request) { // Prefix request with getSequence number and empty envelope String sequenceText = String.format("%d", ++sequence); request.push(sequenceText); request.push(""); // Blast the request to all connected servers int server; for (server = 0; server < servers; server++) { ZMsg msg = request.duplicate(); msg.send(socket); } // Wait for a matching reply to arrive from anywhere // Since we can poll several times, calculate each one ZMsg reply = null; long endtime = System.currentTimeMillis() + GLOBAL_TIMEOUT; Poller poller = ctx.createPoller(1); poller.register(socket, Poller.POLLIN); while (System.currentTimeMillis() < endtime) { poller.poll(endtime - System.currentTimeMillis()); if (poller.pollin(0)) { // Reply is [empty][getSequence][OK] reply = ZMsg.recvMsg(socket); assert (reply.size() == 3); reply.pop(); String sequenceStr = reply.popString(); int sequenceNbr = Integer.parseInt(sequenceStr); if (sequenceNbr == sequence) break; reply.destroy(); } } poller.close(); request.destroy(); return reply; } public static void main(String[] argv) { if (argv.length == 0) { System.out.print("I: syntax: flclient2 ...\n"); System.exit(0); } // Create new freelance client object flclient2 client = new flclient2(); // Connect to each endpoint int argn; for (argn = 0; argn < argv.length; argn++) client.connect(argv[argn]); // Send a bunch of name resolution 'requests', measure time int requests = 10000; long start = System.currentTimeMillis(); while (requests-- > 0) { ZMsg request = new ZMsg(); request.add("random name"); ZMsg reply = client.request(request); if (reply == null) { System.out.print("E: name service not available, aborting\n"); break; } reply.destroy(); } System.out.printf("Average round trip cost: %d usec\n", (int) (System.currentTimeMillis() - start) / 10); client.destroy(); } } jeromq-0.6.0/src/test/java/guide/flclient3.java000066400000000000000000000021471455771126300213430ustar00rootroot00000000000000package guide; import org.zeromq.ZMsg; // Freelance client - Model 3 // Uses flcliapi class to encapsulate Freelance pattern public class flclient3 { public static void main(String[] argv) { // Create new freelance client object flcliapi client = new flcliapi(); // Connect to several endpoints client.connect("tcp://localhost:5555"); client.connect("tcp://localhost:5556"); client.connect("tcp://localhost:5557"); // Send a bunch of name resolution 'requests', measure time int requests = 10000; long start = System.currentTimeMillis(); while (requests-- > 0) { ZMsg request = new ZMsg(); request.add("random name"); ZMsg reply = client.request(request); if (reply == null) { System.out.print("E: name service not available, aborting\n"); break; } reply.destroy(); } System.out.printf("Average round trip cost: %d usec\n", (int) (System.currentTimeMillis() - start) / 10); client.destroy(); } } jeromq-0.6.0/src/test/java/guide/flserver1.java000066400000000000000000000016611455771126300213710ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; // Freelance server - Model 1 // Trivial echo service public class flserver1 { public static void main(String[] args) { if (args.length < 1) { System.out.print("I: syntax: flserver1 \n"); System.exit(0); } try (ZContext ctx = new ZContext()) { Socket server = ctx.createSocket(SocketType.REP); server.bind(args[0]); System.out.printf("I: echo service is ready at %s\n", args[0]); while (true) { ZMsg msg = ZMsg.recvMsg(server); if (msg == null) break; // Interrupted msg.send(server); } if (Thread.currentThread().isInterrupted()) System.out.print("W: interrupted\n"); } } } jeromq-0.6.0/src/test/java/guide/flserver2.java000066400000000000000000000023151455771126300213670ustar00rootroot00000000000000package guide; import org.zeromq.*; import org.zeromq.ZMQ.Socket; // Freelance server - Model 2 // Does some work, replies OK, with message sequencing public class flserver2 { public static void main(String[] args) { if (args.length < 1) { System.out.print("I: syntax: flserver2 \n"); System.exit(0); } try (ZContext ctx = new ZContext()) { Socket server = ctx.createSocket(SocketType.REP); server.bind(args[0]); System.out.printf("I: echo service is ready at %s\n", args[0]); while (true) { ZMsg request = ZMsg.recvMsg(server); if (request == null) break; // Interrupted // Fail nastily if run against wrong client assert (request.size() == 2); ZFrame identity = request.pop(); request.destroy(); ZMsg reply = new ZMsg(); reply.add(identity); reply.add("OK"); reply.send(server); } if (Thread.currentThread().isInterrupted()) System.out.print("W: interrupted\n"); } } } jeromq-0.6.0/src/test/java/guide/flserver3.java000066400000000000000000000035561455771126300214000ustar00rootroot00000000000000package guide; import org.zeromq.*; import org.zeromq.ZMQ.Socket; // Freelance server - Model 3 // Uses an ROUTER/ROUTER socket but just one thread public class flserver3 { public static void main(String[] args) { boolean verbose = (args.length > 0 && args[0].equals("-v")); try (ZContext ctx = new ZContext()) { // Prepare server socket with predictable identity String bindEndpoint = "tcp://*:5555"; String connectEndpoint = "tcp://localhost:5555"; Socket server = ctx.createSocket(SocketType.ROUTER); server.setIdentity(connectEndpoint.getBytes(ZMQ.CHARSET)); server.bind(bindEndpoint); System.out.printf("I: service is ready at %s\n", bindEndpoint); while (!Thread.currentThread().isInterrupted()) { ZMsg request = ZMsg.recvMsg(server); if (verbose && request != null) request.dump(System.out); if (request == null) break; // Interrupted // Frame 0: identity of client // Frame 1: PING, or client control frame // Frame 2: request body ZFrame identity = request.pop(); ZFrame control = request.pop(); ZMsg reply = new ZMsg(); if (control.equals(new ZFrame("PING"))) reply.add("PONG"); else { reply.add(control); reply.add("OK"); } request.destroy(); reply.push(identity); if (verbose && reply != null) reply.dump(System.out); reply.send(server); } if (Thread.currentThread().isInterrupted()) System.out.print("W: interrupted\n"); } } } jeromq-0.6.0/src/test/java/guide/hwclient.java000066400000000000000000000020601455771126300212670ustar00rootroot00000000000000package guide; // // Hello World client in Java // Connects REQ socket to tcp://localhost:5555 // Sends "Hello" to server, expects "World" back // import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; public class hwclient { public static void main(String[] args) { try (ZContext context = new ZContext()) { // Socket to talk to server System.out.println("Connecting to hello world server"); ZMQ.Socket socket = context.createSocket(SocketType.REQ); socket.connect("tcp://localhost:5555"); for (int requestNbr = 0; requestNbr != 10; requestNbr++) { String request = "Hello"; System.out.println("Sending Hello " + requestNbr); socket.send(request.getBytes(ZMQ.CHARSET), 0); byte[] reply = socket.recv(0); System.out.println( "Received " + new String(reply, ZMQ.CHARSET) + " " + requestNbr ); } } } } jeromq-0.6.0/src/test/java/guide/hwserver.java000066400000000000000000000016771455771126300213340ustar00rootroot00000000000000package guide; // // Hello World server in Java // Binds REP socket to tcp://*:5555 // Expects "Hello" from client, replies with "World" // import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; public class hwserver { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { // Socket to talk to clients ZMQ.Socket socket = context.createSocket(SocketType.REP); socket.bind("tcp://*:5555"); while (!Thread.currentThread().isInterrupted()) { byte[] reply = socket.recv(0); System.out.println( "Received " + ": [" + new String(reply, ZMQ.CHARSET) + "]" ); String response = "world"; socket.send(response.getBytes(ZMQ.CHARSET), 0); Thread.sleep(1000); // Do some 'work' } } } } jeromq-0.6.0/src/test/java/guide/identity.java000066400000000000000000000020751455771126300213110ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Demonstrate identities as used by the request-reply pattern. */ public class identity { public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket sink = context.createSocket(SocketType.ROUTER); sink.bind("inproc://example"); // First allow 0MQ to set the identity, [00] + random 4byte Socket anonymous = context.createSocket(SocketType.REQ); anonymous.connect("inproc://example"); anonymous.send("ROUTER uses a generated UUID", 0); ZHelper.dump(sink); // Then set the identity ourself Socket identified = context.createSocket(SocketType.REQ); identified.setIdentity("Hello".getBytes(ZMQ.CHARSET)); identified.connect("inproc://example"); identified.send("ROUTER socket uses REQ's socket identity", 0); ZHelper.dump(sink); } } } jeromq-0.6.0/src/test/java/guide/interrupt.java000066400000000000000000000024431455771126300215130ustar00rootroot00000000000000package guide; /* * * Interrupt in Java * Shows how to handle Ctrl-C */ import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQException; import org.zeromq.ZContext; public class interrupt { public static void main(String[] args) { // Prepare our context and socket final ZContext context = new ZContext(); final Thread zmqThread = new Thread(() -> { ZMQ.Socket socket = context.createSocket(SocketType.REP); socket.bind("tcp://*:5555"); while (!Thread.currentThread().isInterrupted()) { try { socket.recv(0); } catch (ZMQException e) { if (e.getErrorCode() == ZMQ.Error.ETERM.getCode()) { break; } } } socket.setLinger(0); socket.close(); }); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("W: interrupt received, killing server..."); context.close(); try { zmqThread.interrupt(); zmqThread.join(); } catch (InterruptedException e) { } })); zmqThread.start(); } } jeromq-0.6.0/src/test/java/guide/kvmsg.java000066400000000000000000000271211455771126300206060ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.UUID; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; public class kvmsg { // Keys are short strings private static final int KVMSG_KEY_MAX = 255; // Message is formatted on wire as 4 frames: // frame 0: getKey (0MQ string) // frame 1: getSequence (8 bytes, network order) // frame 2: uuid (blob, 16 bytes) // frame 3: properties (0MQ string) // frame 4: body (blob) private static final int FRAME_KEY = 0; private static final int FRAME_SEQ = 1; private static final int FRAME_UUID = 2; private static final int FRAME_PROPS = 3; private static final int FRAME_BODY = 4; private static final int KVMSG_FRAMES = 5; // Presence indicators for each frame private final boolean[] present = new boolean[KVMSG_FRAMES]; // Corresponding 0MQ message frames, if any private final byte[][] frame = new byte[KVMSG_FRAMES][]; // Key, copied into safe string private String key; // List of properties, as name=value strings private final Properties props; private int props_size; // .split property encoding // These two helpers serialize a list of properties to and from a // message frame: private void encodeProps() { ByteBuffer msg = ByteBuffer.allocate(props_size); for (Entry o : props.entrySet()) { String prop = o.getKey().toString() + "=" + o.getValue().toString() + "\n"; msg.put(prop.getBytes(ZMQ.CHARSET)); } present[FRAME_PROPS] = true; frame[FRAME_PROPS] = msg.array(); } private void decodeProps() { byte[] msg = frame[FRAME_PROPS]; props_size = msg.length; props.clear(); if (msg.length == 0) return; System.out.println(msg.length + " :" + new String(msg, ZMQ.CHARSET)); for (String prop : new String(msg, ZMQ.CHARSET).split("\n")) { String[] split = prop.split("="); props.setProperty(split[0], split[1]); } } // .split constructor and destructor // Here are the constructor and destructor for the class: // Constructor, takes a getSequence number for the new kvmsg instance: public kvmsg(long sequence) { props = new Properties(); setSequence(sequence); } public void destroy() { } // .split recv method // This method reads a getKey-value message from the socket and returns a // new {{kvmsg}} instance: public static kvmsg recv(Socket socket) { // This method is almost unchanged from kvsimple // .skip assert (socket != null); kvmsg self = new kvmsg(0); // Read all frames off the wire, reject if bogus int frameNbr; for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) { //zmq_msg_init (&self->frame [frameNbr]); self.present[frameNbr] = true; if ((self.frame[frameNbr] = socket.recv(0)) == null) { self.destroy(); break; } // Verify multipart framing boolean rcvmore = frameNbr < KVMSG_FRAMES - 1; if (socket.hasReceiveMore() != rcvmore) { self.destroy(); break; } } // .until self.decodeProps(); return self; } // Send getKey-value message to socket; any empty frames are sent as such. public void send(Socket socket) { assert (socket != null); encodeProps(); // The rest of the method is unchanged from kvsimple // .skip int frameNbr; for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) { byte[] copy = ZMQ.MESSAGE_SEPARATOR; if (present[frameNbr]) copy = frame[frameNbr]; socket.send(copy, (frameNbr < KVMSG_FRAMES - 1) ? ZMQ.SNDMORE : 0); } } // .until // .split dup method // This method duplicates a {{kvmsg}} instance, returns the new instance: public kvmsg dup() { kvmsg kvmsg = new kvmsg(0); int frameNbr; for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) { if (present[frameNbr]) { kvmsg.frame[frameNbr] = new byte[frame[frameNbr].length]; System.arraycopy(frame[frameNbr], 0, kvmsg.frame[frameNbr], 0, frame[frameNbr].length); kvmsg.present[frameNbr] = true; } } kvmsg.props_size = props_size; kvmsg.props.putAll(props); return kvmsg; } // The getKey, getSequence, body, and size methods are the same as in kvsimple. // .skip // Return getKey from last read message, if any, else NULL public String getKey() { if (present[FRAME_KEY]) { if (key == null) { int size = frame[FRAME_KEY].length; if (size > KVMSG_KEY_MAX) size = KVMSG_KEY_MAX; byte[] buf = new byte[size]; System.arraycopy(frame[FRAME_KEY], 0, buf, 0, size); key = new String(buf, ZMQ.CHARSET); } return key; } else return null; } // Set message getKey as provided public void setKey(String key) { byte[] msg = new byte[key.length()]; System.arraycopy(key.getBytes(ZMQ.CHARSET), 0, msg, 0, key.length()); frame[FRAME_KEY] = msg; present[FRAME_KEY] = true; this.key = null; } // Set message getKey using printf format public void fmtKey(String fmt, Object... args) { setKey(String.format(fmt, args)); } // Return getSequence nbr from last read message, if any public long getSequence() { if (present[FRAME_SEQ]) { assert (frame[FRAME_SEQ].length == 8); ByteBuffer source = ByteBuffer.wrap(frame[FRAME_SEQ]); return source.getLong(); } else return 0; } // Set message getSequence number public void setSequence(long sequence) { ByteBuffer msg = ByteBuffer.allocate(8); msg.putLong(sequence); present[FRAME_SEQ] = true; frame[FRAME_SEQ] = msg.array(); } // Return body from last read message, if any, else NULL public byte[] body() { if (present[FRAME_BODY]) return frame[FRAME_BODY]; else return null; } // Set message body public void setBody(byte[] body) { byte[] msg = new byte[body.length]; System.arraycopy(body, 0, msg, 0, body.length); frame[FRAME_BODY] = msg; present[FRAME_BODY] = true; } // Set message body using printf format public void fmtBody(String fmt, Object... args) { setBody(String.format(fmt, args).getBytes(ZMQ.CHARSET)); } // Return body size from last read message, if any, else zero public int size() { if (present[FRAME_BODY]) return frame[FRAME_BODY].length; else return 0; } // .until // .split UUID methods // These methods get and set the UUID for the getKey-value message: public byte[] UUID() { if (present[FRAME_UUID]) return frame[FRAME_UUID]; else return null; } // Sets the UUID to a randomly generated value public void setUUID() { byte[] msg = UUID.randomUUID().toString().getBytes(ZMQ.CHARSET); present[FRAME_UUID] = true; frame[FRAME_UUID] = msg; } // .split property methods // These methods get and set a specified message property: // Get message property, return "" if no such property is defined. public String getProp(String name) { return props.getProperty(name, ""); } // Set message property. Property name cannot contain '='. Max length of // value is 255 chars. public void setProp(String name, String fmt, Object... args) { String value = String.format(fmt, args); Object old = props.setProperty(name, value); if (old != null) props_size -= old.toString().length(); else props_size += name.length() + 2; props_size += value.length(); } // .split store method // This method stores the getKey-value message into a hash map, unless // the getKey and value are both null. It nullifies the {{kvmsg}} reference // so that the object is owned by the hash map, not the caller: public void store(Map hash) { if (size() > 0) { if (present[FRAME_KEY] && present[FRAME_BODY]) { hash.put(getKey(), this); } } else hash.remove(getKey()); } // .split dump method // This method extends the {{kvsimple}} implementation with support for // message properties: public void dump() { int size = size(); byte[] body = body(); System.err.printf("[seq:%d]", getSequence()); System.err.printf("[getKey:%s]", getKey()); // .until System.err.printf("[size:%d] ", size); System.err.print("["); for (String key : props.stringPropertyNames()) { System.err.printf("%s=%s;", key, props.getProperty(key)); } System.err.print("]"); // .skip for (int charNbr = 0; charNbr < size; charNbr++) System.err.printf("%02X", body[charNbr]); System.err.print("\n"); } // .until // .split test method // This method is the same as in {{kvsimple}} with added support // for the uuid and property features of {{kvmsg}}: public void test(boolean verbose) { System.out.print(" * kvmsg: "); // Prepare our context and sockets try (ZContext ctx = new ZContext()) { Socket output = ctx.createSocket(SocketType.DEALER); output.bind("ipc://kvmsg_selftest.ipc"); Socket input = ctx.createSocket(SocketType.DEALER); input.connect("ipc://kvmsg_selftest.ipc"); Map kvmap = new HashMap<>(); // .until // Test send and receive of simple message kvmsg kvmsg = new kvmsg(1); kvmsg.setKey("getKey"); kvmsg.setUUID(); kvmsg.setBody("body".getBytes(ZMQ.CHARSET)); if (verbose) kvmsg.dump(); kvmsg.send(output); kvmsg.store(kvmap); kvmsg = guide.kvmsg.recv(input); if (verbose) kvmsg.dump(); assert (kvmsg.getKey().equals("getKey")); kvmsg.store(kvmap); // Test send and receive of message with properties kvmsg = new kvmsg(2); kvmsg.setProp("prop1", "value1"); kvmsg.setProp("prop2", "value1"); kvmsg.setProp("prop2", "value2"); kvmsg.setKey("getKey"); kvmsg.setUUID(); kvmsg.setBody("body".getBytes(ZMQ.CHARSET)); assert (kvmsg.getProp("prop2").equals("value2")); if (verbose) kvmsg.dump(); kvmsg.send(output); kvmsg.destroy(); kvmsg = guide.kvmsg.recv(input); if (verbose) kvmsg.dump(); assert (kvmsg.key.equals("getKey")); assert (kvmsg.getProp("prop2").equals("value2")); kvmsg.destroy(); } System.out.print("OK\n"); } // .until } jeromq-0.6.0/src/test/java/guide/kvmsgTest.java000066400000000000000000000006201455771126300214410ustar00rootroot00000000000000package guide; import org.junit.Test; public class kvmsgTest { @Test public void showKmsgIssueTest() { kvmsg msg = new kvmsg(0); msg.setKey("mykey"); kvmsg clone = msg.dup(); clone.setKey(clone.getKey() + "theirkey"); // This should pass but does not in 0.5.3: Reported in jeromq issue #940 assert(clone.getKey().equals("mykeytheirkey")); } } jeromq-0.6.0/src/test/java/guide/kvsimple.java000066400000000000000000000052561455771126300213160ustar00rootroot00000000000000package guide; import java.nio.ByteBuffer; import java.util.Arrays; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; /** * * A simple getKey value message class * @author Danish Shrestha <dshrestha06@gmail.com> * */ public class kvsimple { private final String key; private long sequence; private final byte[] body; public kvsimple(String key, long sequence, byte[] body) { this.key = key; this.sequence = sequence; this.body = body; // clone if needed } public String getKey() { return key; } public long getSequence() { return sequence; } public void setSequence(long sequence) { this.sequence = sequence; } public byte[] getBody() { return body; } public void send(Socket publisher) { publisher.send(key.getBytes(ZMQ.CHARSET), ZMQ.SNDMORE); ByteBuffer bb = ByteBuffer.allocate(8); bb.asLongBuffer().put(sequence); publisher.send(bb.array(), ZMQ.SNDMORE); publisher.send(body, 0); } public static kvsimple recv(Socket updates) { byte[] data = updates.recv(0); if (data == null || !updates.hasReceiveMore()) return null; String key = new String(data, ZMQ.CHARSET); data = updates.recv(0); if (data == null || !updates.hasReceiveMore()) return null; long sequence = ByteBuffer.wrap(data).getLong(); byte[] body = updates.recv(0); if (body == null || updates.hasReceiveMore()) return null; return new kvsimple(key, sequence, body); } @Override public String toString() { return "kvsimple [getKey=" + key + ", getSequence=" + sequence + ", body=" + Arrays.toString(body) + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(body); result = prime * result + ((key == null) ? 0 : key.hashCode()); result = prime * result + (int) (sequence ^ (sequence >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; kvsimple other = (kvsimple) obj; if (!Arrays.equals(body, other.body)) return false; if (key == null) { if (other.key != null) return false; } else if (!key.equals(other.key)) return false; return sequence == other.sequence; } } jeromq-0.6.0/src/test/java/guide/lbbroker.java000066400000000000000000000146311455771126300212630ustar00rootroot00000000000000package guide; import java.util.LinkedList; import java.util.Queue; import org.zeromq.SocketType; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; public class lbbroker { private static final int NBR_CLIENTS = 10; private static final int NBR_WORKERS = 3; /** * Basic request-reply client using REQ socket */ private static class ClientTask extends Thread { @Override public void run() { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket client = context.createSocket(SocketType.REQ); ZHelper.setId(client); // Set a printable identity client.connect("ipc://frontend.ipc"); // Send request, get reply client.send("HELLO"); String reply = client.recvStr(); System.out.println("Client: " + reply); } } } /** * While this example runs in a single process, that is just to make * it easier to start and stop the example. Each thread has its own * context and conceptually acts as a separate process. * This is the worker task, using a REQ socket to do load-balancing. */ private static class WorkerTask extends Thread { @Override public void run() { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.REQ); ZHelper.setId(worker); // Set a printable identity worker.connect("ipc://backend.ipc"); // Tell backend we're ready for work worker.send("READY"); while (!Thread.currentThread().isInterrupted()) { String address = worker.recvStr(); String empty = worker.recvStr(); assert (empty.isEmpty()); // Get request, send reply String request = worker.recvStr(); System.out.println("Worker: " + request); worker.sendMore(address); worker.sendMore(""); worker.send("OK"); } } } } /** * This is the main task. It starts the clients and workers, and then * routes requests between the two layers. Workers signal READY when * they start; after that we treat them as ready when they reply with * a response back to a client. The load-balancing data structure is * just a queue of next available workers. */ public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket frontend = context.createSocket(SocketType.ROUTER); Socket backend = context.createSocket(SocketType.ROUTER); frontend.bind("ipc://frontend.ipc"); backend.bind("ipc://backend.ipc"); int clientNbr; for (clientNbr = 0; clientNbr < NBR_CLIENTS; clientNbr++) new ClientTask().start(); for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) new WorkerTask().start(); // Here is the main loop for the least-recently-used queue. It has // two sockets; a frontend for clients and a backend for workers. // It polls the backend in all cases, and polls the frontend only // when there are one or more workers ready. This is a neat way to // use 0MQ's own queues to hold messages we're not ready to process // yet. When we get a client reply, we pop the next available // worker, and send the request to it, including the originating // client identity. When a worker replies, we re-queue that worker, // and we forward the reply to the original client, using the reply // envelope. // Queue of available workers Queue workerQueue = new LinkedList<>(); while (!Thread.currentThread().isInterrupted()) { // Initialize poll set Poller items = context.createPoller(2); //  Always poll for worker activity on backend items.register(backend, Poller.POLLIN); //  Poll front-end only if we have available workers if (!workerQueue.isEmpty()) items.register(frontend, Poller.POLLIN); if (items.poll() < 0) break; // Interrupted // Handle worker activity on backend if (items.pollin(0)) { // Queue worker address for LRU routing workerQueue.add(backend.recvStr()); // Second frame is empty String empty = backend.recvStr(); assert (empty.isEmpty()); // Third frame is READY or else a client reply address String clientAddr = backend.recvStr(); // If client reply, send rest back to frontend if (!clientAddr.equals("READY")) { empty = backend.recvStr(); assert (empty.isEmpty()); String reply = backend.recvStr(); frontend.sendMore(clientAddr); frontend.sendMore(""); frontend.send(reply); if (--clientNbr == 0) break; } } if (items.pollin(1)) { // Now get next client request, route to LRU worker // Client request is [address][empty][request] String clientAddr = frontend.recvStr(); String empty = frontend.recvStr(); assert (empty.isEmpty()); String request = frontend.recvStr(); String workerAddr = workerQueue.poll(); backend.sendMore(workerAddr); backend.sendMore(""); backend.sendMore(clientAddr); backend.sendMore(""); backend.send(request); } } } } } jeromq-0.6.0/src/test/java/guide/lbbroker2.java000066400000000000000000000116121455771126300213410ustar00rootroot00000000000000package guide; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Load-balancing broker * Demonstrates use of the high level API */ public class lbbroker2 { private static final int NBR_CLIENTS = 10; private static final int NBR_WORKERS = 3; private static final byte[] WORKER_READY = { '\001' }; // Signals worker is ready /** * Basic request-reply client using REQ socket */ private static class ClientTask implements ZThread.IDetachedRunnable { @Override public void run(Object[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket client = context.createSocket(SocketType.REQ); ZHelper.setId(client); // Set a printable identity client.connect("ipc://frontend.ipc"); // Send request, get reply client.send("HELLO"); String reply = client.recvStr(); System.out.println("Client: " + reply); } } } /** * Worker using REQ socket to do load-balancing */ private static class WorkerTask implements ZThread.IDetachedRunnable { @Override public void run(Object[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.REQ); ZHelper.setId(worker); // Set a printable identity worker.connect("ipc://backend.ipc"); // Tell backend we're ready for work ZFrame frame = new ZFrame(WORKER_READY); frame.send(worker, 0); while (true) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; msg.getLast().reset("OK"); msg.send(worker); } } } } /** * This is the main task. This has the identical functionality to * the previous lbbroker example but uses higher level classes to start child threads * to hold the list of workers, and to read and send messages: */ public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket frontend = context.createSocket(SocketType.ROUTER); Socket backend = context.createSocket(SocketType.ROUTER); frontend.bind("ipc://frontend.ipc"); backend.bind("ipc://backend.ipc"); int clientNbr; for (clientNbr = 0; clientNbr < NBR_CLIENTS; clientNbr++) ZThread.start(new ClientTask()); for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) ZThread.start(new WorkerTask()); // Queue of available workers Queue workerQueue = new LinkedList<>(); // Here is the main loop for the load-balancer. It works the same // way as the previous example, but is a lot shorter because ZMsg // class gives us an API that does more with fewer calls: while (!Thread.currentThread().isInterrupted()) { // Initialize poll set Poller items = context.createPoller(2); //  Always poll for worker activity on backend items.register(backend, Poller.POLLIN); //  Poll front-end only if we have available workers if (!workerQueue.isEmpty()) items.register(frontend, Poller.POLLIN); if (items.poll() < 0) break; // Interrupted // Handle worker activity on backend if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(backend); if (msg == null) break; // Interrupted ZFrame identity = msg.unwrap(); // Queue worker address for LRU routing workerQueue.add(identity); // Forward message to client if it's not a READY ZFrame frame = msg.getFirst(); if (Arrays.equals(frame.getData(), WORKER_READY)) msg.destroy(); else msg.send(frontend); } if (items.pollin(1)) { // Get client request, route to first available worker ZMsg msg = ZMsg.recvMsg(frontend); if (msg != null) { msg.wrap(workerQueue.poll()); msg.send(backend); } } } } } } jeromq-0.6.0/src/test/java/guide/lbbroker3.java000066400000000000000000000132331455771126300213430ustar00rootroot00000000000000package guide; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import org.zeromq.*; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Socket; /** * Load-balancing broker * Demonstrates use of the ZLoop API and reactor style *

* The client and worker tasks are identical from the previous example. */ public class lbbroker3 { private static final int NBR_CLIENTS = 10; private static final int NBR_WORKERS = 3; private static final byte[] WORKER_READY = { '\001' }; /** * Basic request-reply client using REQ socket */ private static class ClientTask implements ZThread.IDetachedRunnable { @Override public void run(Object [] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket client = context.createSocket(SocketType.REQ); ZHelper.setId(client); // Set a printable identity client.connect("ipc://frontend.ipc"); // Send request, get reply client.send("HELLO"); String reply = client.recvStr(); System.out.println("Client: " + reply); } } } /** * Worker using REQ socket to do load-balancing */ private static class WorkerTask implements ZThread.IDetachedRunnable { @Override public void run(Object [] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.REQ); ZHelper.setId(worker); // Set a printable identity worker.connect("ipc://backend.ipc"); // Tell backend we're ready for work ZFrame frame = new ZFrame(WORKER_READY); frame.send(worker, 0); while (true) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; msg.getLast().reset("OK"); msg.send(worker); } } } } //Our load-balancer structure, passed to reactor handlers private static class LBBroker { Socket frontend; // Listen to clients Socket backend; // Listen to workers Queue workers; // List of ready workers } /** * In the reactor design, each time a message arrives on a socket, the * reactor passes it to a handler function. We have two handlers; one * for the frontend, one for the backend: */ private static class FrontendHandler implements ZLoop.IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg_) { LBBroker arg = (LBBroker) arg_; ZMsg msg = ZMsg.recvMsg(arg.frontend); if (msg != null) { msg.wrap(arg.workers.poll()); msg.send(arg.backend); // Cancel reader on frontend if we went from 1 to 0 workers if (arg.workers.isEmpty()) { loop.removePoller(new PollItem(arg.frontend, 0)); } } return 0; } } private static class BackendHandler implements ZLoop.IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg_) { LBBroker arg = (LBBroker) arg_; ZMsg msg = ZMsg.recvMsg(arg.backend); if (msg != null) { ZFrame address = msg.unwrap(); // Queue worker address for load-balancing arg.workers.add(address); // Enable reader on frontend if we went from 0 to 1 workers if (arg.workers.size() == 1) { PollItem newItem = new PollItem(arg.frontend, ZMQ.Poller.POLLIN); loop.addPoller(newItem, frontendHandler, arg); } // Forward message to client if it's not a READY ZFrame frame = msg.getFirst(); if (Arrays.equals(frame.getData(), WORKER_READY)) msg.destroy(); else msg.send(arg.frontend); } return 0; } } private final static FrontendHandler frontendHandler = new FrontendHandler(); private final static BackendHandler backendHandler = new BackendHandler(); /** * And the main task now sets-up child tasks, then starts its reactor. * If you press Ctrl-C, the reactor exits and the main task shuts down. */ public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { LBBroker arg = new LBBroker(); arg.frontend = context.createSocket(SocketType.ROUTER); arg.backend = context.createSocket(SocketType.ROUTER); arg.frontend.bind("ipc://frontend.ipc"); arg.backend.bind("ipc://backend.ipc"); int clientNbr; for (clientNbr = 0; clientNbr < NBR_CLIENTS; clientNbr++) ZThread.start(new ClientTask()); for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) ZThread.start(new WorkerTask()); // Queue of available workers arg.workers = new LinkedList<>(); // Prepare reactor and fire it up ZLoop reactor = new ZLoop(context); PollItem item = new PollItem(arg.backend, ZMQ.Poller.POLLIN); reactor.addPoller(item, backendHandler, arg); reactor.start(); } } } jeromq-0.6.0/src/test/java/guide/lpclient.java000066400000000000000000000072051455771126300212720ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // // Lazy Pirate client // Use zmq_poll to do a safe request-reply // To run, start lpserver and then randomly kill/restart it // public class lpclient { private final static int REQUEST_TIMEOUT = 2500; // msecs, (> 1000!) private final static int REQUEST_RETRIES = 3; // Before we abandon private final static String SERVER_ENDPOINT = "tcp://localhost:5555"; public static void main(String[] argv) { try (ZContext ctx = new ZContext()) { System.out.println("I: connecting to server"); Socket client = ctx.createSocket(SocketType.REQ); assert (client != null); client.connect(SERVER_ENDPOINT); Poller poller = ctx.createPoller(1); poller.register(client, Poller.POLLIN); int sequence = 0; int retriesLeft = REQUEST_RETRIES; while (retriesLeft > 0 && !Thread.currentThread().isInterrupted()) { // We send a request, then we work to get a reply String request = String.format("%d", ++sequence); client.send(request); int expect_reply = 1; while (expect_reply > 0) { // Poll socket for a reply, with timeout int rc = poller.poll(REQUEST_TIMEOUT); if (rc == -1) break; // Interrupted // Here we process a server reply and exit our loop if the // reply is valid. If we didn't a reply we close the client // socket and resend the request. We try a number of times // before finally abandoning: if (poller.pollin(0)) { // We got a reply from the server, must match // getSequence String reply = client.recvStr(); if (reply == null) break; // Interrupted if (Integer.parseInt(reply) == sequence) { System.out.printf( "I: server replied OK (%s)\n", reply ); retriesLeft = REQUEST_RETRIES; expect_reply = 0; } else System.out.printf( "E: malformed reply from server: %s\n", reply ); } else if (--retriesLeft == 0) { System.out.println( "E: server seems to be offline, abandoning\n" ); break; } else { System.out.println( "W: no response from server, retrying\n" ); // Old socket is confused; close it and open a new one poller.unregister(client); client.close(); System.out.println("I: reconnecting to server\n"); client = ctx.createSocket(SocketType.REQ); client.connect(SERVER_ENDPOINT); poller.register(client, Poller.POLLIN); // Send request again, on new socket client.send(request); } } } } } } jeromq-0.6.0/src/test/java/guide/lpserver.java000066400000000000000000000025271455771126300213240ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; // // Lazy Pirate server // Binds REQ socket to tcp://*:5555 // Like hwserver except: // - echoes request as-is // - randomly runs slowly, or exits to simulate a crash. // public class lpserver { public static void main(String[] argv) throws Exception { Random rand = new Random(System.nanoTime()); try (ZContext context = new ZContext()) { Socket server = context.createSocket(SocketType.REP); server.bind("tcp://*:5555"); int cycles = 0; while (true) { String request = server.recvStr(); cycles++; // Simulate various problems, after a few cycles if (cycles > 3 && rand.nextInt(3) == 0) { System.out.println("I: simulating a crash"); break; } else if (cycles > 3 && rand.nextInt(3) == 0) { System.out.println("I: simulating CPU overload"); Thread.sleep(2000); } System.out.printf("I: normal request (%s)\n", request); Thread.sleep(1000); // Do some heavy work server.send(request); } } } } jeromq-0.6.0/src/test/java/guide/lruqueue3.java000066400000000000000000000126211455771126300214100ustar00rootroot00000000000000package guide; import java.util.LinkedList; import java.util.Queue; import org.zeromq.*; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Socket; class ClientThread3 extends Thread { @Override public void run() { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket client = context.createSocket(SocketType.REQ); // Initialize random number generator client.connect("ipc://frontend.ipc"); // Send request, get reply while (true) { client.send("HELLO".getBytes(ZMQ.CHARSET), 0); byte[] data = client.recv(0); if (data == null) break; String reply = new String(data, ZMQ.CHARSET); try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println( Thread.currentThread().getName() + " Client Sent HELLO" ); } } } } class WorkerThread3 extends Thread { @Override public void run() { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.REQ); worker.connect("ipc://backend.ipc"); ZFrame frame = new ZFrame(lruqueue3.LRU_READY); // Tell backend we're ready for work frame.send(worker, 0); while (true) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; msg.getLast().reset("OK".getBytes(ZMQ.CHARSET)); msg.send(worker); System.out.println( Thread.currentThread().getName() + " Worker Sent OK" ); } } } } //Our LRU queue structure, passed to reactor handlers class LRUQueueArg { Socket frontend; // Listen to clients Socket backend; // Listen to workers Queue workers; // List of ready workers } //In the reactor design, each time a message arrives on a socket, the //reactor passes it to a handler function. We have two handlers; one //for the frontend, one for the backend: class FrontendHandler implements ZLoop.IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg_) { LRUQueueArg arg = (LRUQueueArg) arg_; ZMsg msg = ZMsg.recvMsg(arg.frontend); if (msg != null) { msg.wrap(arg.workers.poll()); msg.send(arg.backend); // Cancel reader on frontend if we went from 1 to 0 workers if (arg.workers.isEmpty()) { PollItem poller = new PollItem(arg.frontend, ZMQ.Poller.POLLIN); loop.removePoller(poller); } } return 0; } } class BackendHandler implements ZLoop.IZLoopHandler { @Override public int handle(ZLoop loop, PollItem item, Object arg_) { LRUQueueArg arg = (LRUQueueArg) arg_; ZMsg msg = ZMsg.recvMsg(arg.backend); if (msg != null) { ZFrame address = msg.unwrap(); // Queue worker address for LRU routing arg.workers.add(address); // Enable reader on frontend if we went from 0 to 1 workers if (arg.workers.size() == 1) { PollItem poller = new PollItem(arg.frontend, ZMQ.Poller.POLLIN); loop.addPoller(poller, lruqueue3.handle_frontend, arg); } // Forward message to client if it's not a READY ZFrame frame = msg.getFirst(); if (new String(frame.getData(), ZMQ.CHARSET).equals(lruqueue3.LRU_READY)) msg.destroy(); else msg.send(arg.frontend); } return 0; } } public class lruqueue3 { public final static String LRU_READY = "\001"; protected final static FrontendHandler handle_frontend = new FrontendHandler(); protected final static BackendHandler handle_backend = new BackendHandler(); public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { LRUQueueArg arg = new LRUQueueArg(); Socket frontend = context.createSocket(SocketType.ROUTER); Socket backend = context.createSocket(SocketType.ROUTER); arg.frontend = frontend; arg.backend = backend; frontend.bind("ipc://frontend.ipc"); backend.bind("ipc://backend.ipc"); int client_nbr; for (client_nbr = 0; client_nbr < 10; client_nbr++) new ClientThread3().start(); int worker_nbr; for (worker_nbr = 0; worker_nbr < 3; worker_nbr++) new WorkerThread3().start(); // Queue of available workers arg.workers = new LinkedList<>(); // Prepare reactor and fire it up ZLoop reactor = new ZLoop(context); reactor.verbose(true); PollItem poller = new PollItem(arg.backend, ZMQ.Poller.POLLIN); reactor.addPoller(poller, handle_backend, arg); reactor.start(); for (ZFrame frame : arg.workers) { frame.destroy(); } } System.exit(0); } } jeromq-0.6.0/src/test/java/guide/lvcache.java000066400000000000000000000053431455771126300210660ustar00rootroot00000000000000package guide; import java.util.HashMap; import java.util.Map; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // Last value cache // Uses XPUB subscription messages to re-send data public class lvcache { public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket frontend = context.createSocket(SocketType.SUB); frontend.bind("tcp://*:5557"); Socket backend = context.createSocket(SocketType.XPUB); backend.bind("tcp://*:5558"); // Subscribe to every single topic from publisher frontend.subscribe(ZMQ.SUBSCRIPTION_ALL); // Store last instance of each topic in a cache Map cache = new HashMap<>(); Poller poller = context.createPoller(2); poller.register(frontend, Poller.POLLIN); poller.register(backend, Poller.POLLIN); // .split main poll loop // We route topic updates from frontend to backend, and we handle // subscriptions by sending whatever we cached, if anything: while (true) { if (poller.poll(1000) == -1) break; // Interrupted // Any new topic data we cache and then forward if (poller.pollin(0)) { String topic = frontend.recvStr(); String current = frontend.recvStr(); if (topic == null) break; cache.put(topic, current); backend.sendMore(topic); backend.send(current); } // .split handle subscriptions // When we get a new subscription, we pull data from the cache: if (poller.pollin(1)) { ZFrame frame = ZFrame.recvFrame(backend); if (frame == null) break; // Event is one byte 0=unsub or 1=sub, followed by topic byte[] event = frame.getData(); if (event[0] == 1) { String topic = new String(event, 1, event.length - 1, ZMQ.CHARSET); System.out.printf("Sending cached topic %s\n", topic); String previous = cache.get(topic); if (previous != null) { backend.sendMore(topic); backend.send(previous); } } frame.destroy(); } } } } } jeromq-0.6.0/src/test/java/guide/mdbroker.java000066400000000000000000000307211455771126300212640ustar00rootroot00000000000000package guide; import java.util.ArrayDeque; import java.util.Deque; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import org.zeromq.*; /** * Majordomo Protocol broker * A minimal implementation of http://rfc.zeromq.org/spec:7 and spec:8 */ public class mdbroker { // We'd normally pull these from config data private static final String INTERNAL_SERVICE_PREFIX = "mmi."; private static final int HEARTBEAT_LIVENESS = 3; // 3-5 is reasonable private static final int HEARTBEAT_INTERVAL = 2500; // msecs private static final int HEARTBEAT_EXPIRY = HEARTBEAT_INTERVAL * HEARTBEAT_LIVENESS; // --------------------------------------------------------------------- /** * This defines a single service. */ private static class Service { public final String name; // Service name final Deque requests; // List of client requests final Deque waiting; // List of waiting workers public Service(String name) { this.name = name; this.requests = new ArrayDeque<>(); this.waiting = new ArrayDeque<>(); } } /** * This defines one worker, idle or active. */ private static class Worker { final String identity;// Identity of worker final ZFrame address; // Address frame to route to Service service; // Owning service, if known long expiry; // Expires at unless heartbeat public Worker(String identity, ZFrame address) { this.address = address; this.identity = identity; this.expiry = System.currentTimeMillis() + HEARTBEAT_INTERVAL * HEARTBEAT_LIVENESS; } } // --------------------------------------------------------------------- private final ZContext ctx; // Our context private final ZMQ.Socket socket; // Socket for clients & workers private long heartbeatAt;// When to send HEARTBEAT private final Map services; // known services private final Map workers; // known workers private final Deque waiting; // idle workers private final boolean verbose; // Print activity to stdout private final Formatter log = new Formatter(System.out); // --------------------------------------------------------------------- /** * Main method - create and start new broker. */ public static void main(String[] args) { mdbroker broker = new mdbroker(args.length > 0 && "-v".equals(args[0])); // Can be called multiple times with different endpoints broker.bind("tcp://*:5555"); broker.mediate(); } /** * Initialize broker state. */ public mdbroker(boolean verbose) { this.verbose = verbose; this.services = new HashMap<>(); this.workers = new HashMap<>(); this.waiting = new ArrayDeque<>(); this.heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; this.ctx = new ZContext(); this.socket = ctx.createSocket(SocketType.ROUTER); } // --------------------------------------------------------------------- /** * Main broker work happens here */ public void mediate() { while (!Thread.currentThread().isInterrupted()) { ZMQ.Poller items = ctx.createPoller(1); items.register(socket, ZMQ.Poller.POLLIN); if (items.poll(HEARTBEAT_INTERVAL) == -1) break; // Interrupted if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(socket); if (msg == null) break; // Interrupted if (verbose) { log.format("I: received message:\n"); msg.dump(log.out()); } ZFrame sender = msg.pop(); ZFrame empty = msg.pop(); ZFrame header = msg.pop(); if (MDP.C_CLIENT.frameEquals(header)) { processClient(sender, msg); } else if (MDP.W_WORKER.frameEquals(header)) processWorker(sender, msg); else { log.format("E: invalid message:\n"); msg.dump(log.out()); msg.destroy(); } sender.destroy(); empty.destroy(); header.destroy(); } items.close(); purgeWorkers(); sendHeartbeats(); } destroy(); // interrupted } /** * Disconnect all workers, destroy context. */ private void destroy() { Worker[] deleteList = workers.values().toArray(new Worker[0]); for (Worker worker : deleteList) { deleteWorker(worker, true); } ctx.destroy(); } /** * Process a request coming from a client. */ private void processClient(ZFrame sender, ZMsg msg) { assert (msg.size() >= 2); // Service name + body ZFrame serviceFrame = msg.pop(); // Set reply return address to client sender msg.wrap(sender.duplicate()); if (serviceFrame.toString().startsWith(INTERNAL_SERVICE_PREFIX)) serviceInternal(serviceFrame, msg); else dispatch(requireService(serviceFrame), msg); serviceFrame.destroy(); } /** * Process message sent to us by a worker. */ private void processWorker(ZFrame sender, ZMsg msg) { assert (!msg.isEmpty()); // At least, command ZFrame command = msg.pop(); boolean workerReady = workers.containsKey(sender.strhex()); Worker worker = requireWorker(sender); if (MDP.W_READY.frameEquals(command)) { // Not first command in session || Reserved service name if (workerReady || sender.toString().startsWith(INTERNAL_SERVICE_PREFIX)) deleteWorker(worker, true); else { // Attach worker to service and mark as idle ZFrame serviceFrame = msg.pop(); worker.service = requireService(serviceFrame); workerWaiting(worker); serviceFrame.destroy(); } } else if (MDP.W_REPLY.frameEquals(command)) { if (workerReady) { // Remove & save client return envelope and insert the // protocol header and service name, then rewrap envelope. ZFrame client = msg.unwrap(); msg.addFirst(worker.service.name); msg.addFirst(MDP.C_CLIENT.newFrame()); msg.wrap(client); msg.send(socket); workerWaiting(worker); } else { deleteWorker(worker, true); } } else if (MDP.W_HEARTBEAT.frameEquals(command)) { if (workerReady) { worker.expiry = System.currentTimeMillis() + HEARTBEAT_EXPIRY; } else { deleteWorker(worker, true); } } else if (MDP.W_DISCONNECT.frameEquals(command)) deleteWorker(worker, false); else { log.format("E: invalid message:\n"); msg.dump(log.out()); } msg.destroy(); } /** * Deletes worker from all data structures, and destroys worker. */ private void deleteWorker(Worker worker, boolean disconnect) { assert (worker != null); if (disconnect) { sendToWorker(worker, MDP.W_DISCONNECT, null, null); } if (worker.service != null) worker.service.waiting.remove(worker); workers.remove(worker); worker.address.destroy(); } /** * Finds the worker (creates if necessary). */ private Worker requireWorker(ZFrame address) { assert (address != null); String identity = address.strhex(); Worker worker = workers.get(identity); if (worker == null) { worker = new Worker(identity, address.duplicate()); workers.put(identity, worker); if (verbose) log.format("I: registering new worker: %s\n", identity); } return worker; } /** * Locates the service (creates if necessary). */ private Service requireService(ZFrame serviceFrame) { assert (serviceFrame != null); String name = serviceFrame.toString(); Service service = services.get(name); if (service == null) { service = new Service(name); services.put(name, service); } return service; } /** * Bind broker to endpoint, can call this multiple times. We use a single * socket for both clients and workers. */ private void bind(String endpoint) { socket.bind(endpoint); log.format("I: MDP broker/0.1.1 is active at %s\n", endpoint); } /** * Handle internal service according to 8/MMI specification */ private void serviceInternal(ZFrame serviceFrame, ZMsg msg) { String returnCode = "501"; if ("mmi.service".equals(serviceFrame.toString())) { String name = msg.peekLast().toString(); returnCode = services.containsKey(name) ? "200" : "400"; } msg.peekLast().reset(returnCode.getBytes(ZMQ.CHARSET)); // Remove & save client return envelope and insert the // protocol header and service name, then rewrap envelope. ZFrame client = msg.unwrap(); msg.addFirst(serviceFrame.duplicate()); msg.addFirst(MDP.C_CLIENT.newFrame()); msg.wrap(client); msg.send(socket); } /** * Send heartbeats to idle workers if it's time */ public synchronized void sendHeartbeats() { // Send heartbeats to idle workers if it's time if (System.currentTimeMillis() >= heartbeatAt) { for (Worker worker : waiting) { sendToWorker(worker, MDP.W_HEARTBEAT, null, null); } heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; } } /** * Look for & kill expired workers. Workers are oldest to most recent, so we * stop at the first alive worker. */ public synchronized void purgeWorkers() { for (Worker w = waiting.peekFirst(); w != null && w.expiry < System.currentTimeMillis(); w = waiting.peekFirst()) { log.format("I: deleting expired worker: %s\n", w.identity); deleteWorker(waiting.pollFirst(), false); } } /** * This worker is now waiting for work. */ public synchronized void workerWaiting(Worker worker) { // Queue to broker and service waiting lists waiting.addLast(worker); worker.service.waiting.addLast(worker); worker.expiry = System.currentTimeMillis() + HEARTBEAT_EXPIRY; dispatch(worker.service, null); } /** * Dispatch requests to waiting workers as possible */ private void dispatch(Service service, ZMsg msg) { assert (service != null); if (msg != null)// Queue message if any service.requests.offerLast(msg); purgeWorkers(); while (!service.waiting.isEmpty() && !service.requests.isEmpty()) { msg = service.requests.pop(); Worker worker = service.waiting.pop(); waiting.remove(worker); sendToWorker(worker, MDP.W_REQUEST, null, msg); msg.destroy(); } } /** * Send message to worker. If message is provided, sends that message. Does * not destroy the message, this is the caller's job. */ public void sendToWorker(Worker worker, MDP command, String option, ZMsg msgp) { ZMsg msg = msgp == null ? new ZMsg() : msgp.duplicate(); // Stack protocol envelope to start of message if (option != null) msg.addFirst(new ZFrame(option)); msg.addFirst(command.newFrame()); msg.addFirst(MDP.W_WORKER.newFrame()); // Stack routing envelope to start of message msg.wrap(worker.address.duplicate()); if (verbose) { log.format("I: sending %s to worker\n", command); msg.dump(log.out()); } msg.send(socket); } } jeromq-0.6.0/src/test/java/guide/mdcliapi.java000066400000000000000000000066561455771126300212530ustar00rootroot00000000000000package guide; import java.util.Formatter; import org.zeromq.*; /** * Majordomo Protocol Client API, Java version Implements the MDP/Worker spec at * http://rfc.zeromq.org/spec:7. * */ public class mdcliapi { private final String broker; private final ZContext ctx; private ZMQ.Socket client; private long timeout = 2500; private int retries = 3; private final boolean verbose; private final Formatter log = new Formatter(System.out); public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } public int getRetries() { return retries; } public void setRetries(int retries) { this.retries = retries; } public mdcliapi(String broker, boolean verbose) { this.broker = broker; this.verbose = verbose; ctx = new ZContext(); reconnectToBroker(); } /** * Connect or reconnect to broker */ void reconnectToBroker() { if (client != null) { client.close(); } client = ctx.createSocket(SocketType.REQ); client.connect(broker); if (verbose) log.format("I: connecting to broker at %s\n", broker); } /** * Send request to broker and get reply by hook or crook Takes ownership of * request message and destroys it when sent. Returns the reply message or * NULL if there was no reply. * * @param service * @param request * @return */ public ZMsg send(String service, ZMsg request) { request.push(new ZFrame(service)); request.push(MDP.C_CLIENT.newFrame()); if (verbose) { log.format("I: send request to '%s' service: \n", service); request.dump(log.out()); } ZMsg reply = null; int retriesLeft = retries; while (retriesLeft > 0 && !Thread.currentThread().isInterrupted()) { request.duplicate().send(client); // Poll socket for a reply, with timeout ZMQ.Poller items = ctx.createPoller(1); items.register(client, ZMQ.Poller.POLLIN); if (items.poll(timeout) == -1) break; // Interrupted if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(client); if (verbose) { log.format("I: received reply: \n"); msg.dump(log.out()); } // Don't try to handle errors, just assert noisily assert (msg.size() >= 3); ZFrame header = msg.pop(); assert (MDP.C_CLIENT.equals(header.toString())); header.destroy(); ZFrame replyService = msg.pop(); assert (service.equals(replyService.toString())); replyService.destroy(); reply = msg; break; } else { items.unregister(client); if (--retriesLeft == 0) { log.format("W: permanent error, abandoning\n"); break; } log.format("W: no reply, reconnecting\n"); reconnectToBroker(); } items.close(); } request.destroy(); return reply; } public void destroy() { ctx.destroy(); } } jeromq-0.6.0/src/test/java/guide/mdcliapi2.java000066400000000000000000000062121455771126300213210ustar00rootroot00000000000000package guide; import java.util.Formatter; import org.zeromq.*; /** * Majordomo Protocol Client API, asynchronous Java version. Implements the * MDP/Worker spec at http://rfc.zeromq.org/spec:7. */ public class mdcliapi2 { private final String broker; private final ZContext ctx; private ZMQ.Socket client; private long timeout = 2500; private final boolean verbose; private final Formatter log = new Formatter(System.out); public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } public mdcliapi2(String broker, boolean verbose) { this.broker = broker; this.verbose = verbose; ctx = new ZContext(); reconnectToBroker(); } /** * Connect or reconnect to broker */ void reconnectToBroker() { if (client != null) { client.close(); } client = ctx.createSocket(SocketType.DEALER); client.connect(broker); if (verbose) log.format("I: connecting to broker at %s...\n", broker); } /** * Returns the reply message or NULL if there was no reply. Does not attempt * to recover from a broker failure, this is not possible without storing * all unanswered requests and resending them all… */ public ZMsg recv() { ZMsg reply = null; // Poll socket for a reply, with timeout ZMQ.Poller items = ctx.createPoller(1); items.register(client, ZMQ.Poller.POLLIN); if (items.poll(timeout * 1000) == -1) return null; // Interrupted if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(client); if (verbose) { log.format("I: received reply: \n"); msg.dump(log.out()); } // Don't try to handle errors, just assert noisily assert (msg.size() >= 4); ZFrame empty = msg.pop(); assert (empty.getData().length == 0); empty.destroy(); ZFrame header = msg.pop(); assert (MDP.C_CLIENT.equals(header.toString())); header.destroy(); ZFrame replyService = msg.pop(); replyService.destroy(); reply = msg; } items.close(); return reply; } /** * Send request to broker and get reply by hook or crook Takes ownership of * request message and destroys it when sent. */ public boolean send(String service, ZMsg request) { assert (request != null); // Prefix request with protocol frames // Frame 0: empty (REQ emulation) // Frame 1: "MDPCxy" (six bytes, MDP/Client x.y) // Frame 2: Service name (printable string) request.addFirst(service); request.addFirst(MDP.C_CLIENT.newFrame()); request.addFirst(""); if (verbose) { log.format("I: send request to '%s' service: \n", service); request.dump(log.out()); } return request.send(client); } public void destroy() { ctx.destroy(); } } jeromq-0.6.0/src/test/java/guide/mdclient.java000066400000000000000000000014431455771126300212550ustar00rootroot00000000000000package guide; import org.zeromq.ZMsg; /** * Majordomo Protocol client example. Uses the mdcli API to hide all MDP aspects */ public class mdclient { public static void main(String[] args) { boolean verbose = (args.length > 0 && "-v".equals(args[0])); mdcliapi clientSession = new mdcliapi("tcp://localhost:5555", verbose); int count; for (count = 0; count < 100000; count++) { ZMsg request = new ZMsg(); request.addString("Hello world"); ZMsg reply = clientSession.send("echo", request); if (reply != null) reply.destroy(); else break; // Interrupt or failure } System.out.printf("%d requests/replies processed\n", count); clientSession.destroy(); } } jeromq-0.6.0/src/test/java/guide/mdclient2.java000066400000000000000000000016311455771126300213360ustar00rootroot00000000000000package guide; import org.zeromq.ZMsg; /** * Majordomo Protocol client example, asynchronous. Uses the mdcli API to hide * all MDP aspects */ public class mdclient2 { public static void main(String[] args) { boolean verbose = (args.length > 0 && "-v".equals(args[0])); mdcliapi2 clientSession = new mdcliapi2("tcp://localhost:5555", verbose); int count; for (count = 0; count < 100000; count++) { ZMsg request = new ZMsg(); request.addString("Hello world"); clientSession.send("echo", request); } for (count = 0; count < 100000; count++) { ZMsg reply = clientSession.recv(); if (reply != null) reply.destroy(); else break; // Interrupt or failure } System.out.printf("%d requests/replies processed\n", count); clientSession.destroy(); } } jeromq-0.6.0/src/test/java/guide/mdworker.java000066400000000000000000000013111455771126300213020ustar00rootroot00000000000000package guide; import org.zeromq.ZMsg; /** * Majordomo Protocol worker example. Uses the mdwrk API to hide all MDP aspects * */ public class mdworker { /** * @param args */ public static void main(String[] args) { boolean verbose = (args.length > 0 && "-v".equals(args[0])); mdwrkapi workerSession = new mdwrkapi("tcp://localhost:5555", "echo", verbose); ZMsg reply = null; while (!Thread.currentThread().isInterrupted()) { ZMsg request = workerSession.receive(reply); if (request == null) break; //Interrupted reply = request; // Echo is complex :-) } workerSession.destroy(); } } jeromq-0.6.0/src/test/java/guide/mdwrkapi.java000066400000000000000000000145351455771126300213020ustar00rootroot00000000000000package guide; import java.util.Formatter; import org.zeromq.*; /** * Majordomo Protocol Client API, Java version Implements the MDP/Worker spec at * http://rfc.zeromq.org/spec:7. */ public class mdwrkapi { private static final int HEARTBEAT_LIVENESS = 3; // 3-5 is reasonable private final String broker; private final ZContext ctx; private final String service; private ZMQ.Socket worker; // Socket to broker private long heartbeatAt; // When to send HEARTBEAT private int liveness; // How many attempts left private int heartbeat = 2500; // Heartbeat delay, msecs private int reconnect = 2500; // Reconnect delay, msecs // Internal state private boolean expectReply = false; // false only at start private final boolean verbose; // Print activity to stdout private final Formatter log = new Formatter(System.out); // Return address, if any private ZFrame replyTo; public mdwrkapi(String broker, String service, boolean verbose) { assert (broker != null); assert (service != null); this.broker = broker; this.service = service; this.verbose = verbose; ctx = new ZContext(); reconnectToBroker(); } /** * Send message to broker If no msg is provided, creates one internally * * @param command * @param option * @param msg */ void sendToBroker(MDP command, String option, ZMsg msg) { msg = msg != null ? msg.duplicate() : new ZMsg(); // Stack protocol envelope to start of message if (option != null) msg.addFirst(new ZFrame(option)); msg.addFirst(command.newFrame()); msg.addFirst(MDP.W_WORKER.newFrame()); msg.addFirst(new ZFrame(ZMQ.MESSAGE_SEPARATOR)); if (verbose) { log.format("I: sending %s to broker\n", command); msg.dump(log.out()); } msg.send(worker); } /** * Connect or reconnect to broker */ void reconnectToBroker() { if (worker != null) { worker.close(); } worker = ctx.createSocket(SocketType.DEALER); worker.connect(broker); if (verbose) log.format("I: connecting to broker at %s\n", broker); // Register service with broker sendToBroker(MDP.W_READY, service, null); // If liveness hits zero, queue is considered disconnected liveness = HEARTBEAT_LIVENESS; heartbeatAt = System.currentTimeMillis() + heartbeat; } /** * Send reply, if any, to broker and wait for next request. */ public ZMsg receive(ZMsg reply) { // Format and send the reply if we were provided one assert (reply != null || !expectReply); if (reply != null) { assert (replyTo != null); reply.wrap(replyTo); sendToBroker(MDP.W_REPLY, null, reply); reply.destroy(); } expectReply = true; while (!Thread.currentThread().isInterrupted()) { // Poll socket for a reply, with timeout ZMQ.Poller items = ctx.createPoller(1); items.register(worker, ZMQ.Poller.POLLIN); long timeout = 2500; if (items.poll(timeout) == -1) break; // Interrupted if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; // Interrupted if (verbose) { log.format("I: received message from broker: \n"); msg.dump(log.out()); } liveness = HEARTBEAT_LIVENESS; // Don't try to handle errors, just assert noisily assert (msg != null && msg.size() >= 3); ZFrame empty = msg.pop(); assert (empty.getData().length == 0); empty.destroy(); ZFrame header = msg.pop(); assert (MDP.W_WORKER.frameEquals(header)); header.destroy(); ZFrame command = msg.pop(); if (MDP.W_REQUEST.frameEquals(command)) { // We should pop and save as many addresses as there are // up to a null part, but for now, just save one replyTo = msg.unwrap(); command.destroy(); return msg; // We have a request to process } else if (MDP.W_HEARTBEAT.frameEquals(command)) { // Do nothing for heartbeats } else if (MDP.W_DISCONNECT.frameEquals(command)) { reconnectToBroker(); } else { log.format("E: invalid input message: \n"); msg.dump(log.out()); } command.destroy(); msg.destroy(); } else if (--liveness == 0) { if (verbose) log.format("W: disconnected from broker - retrying\n"); try { Thread.sleep(reconnect); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Restore the // interrupted status break; } reconnectToBroker(); } // Send HEARTBEAT if it's time if (System.currentTimeMillis() > heartbeatAt) { sendToBroker(MDP.W_HEARTBEAT, null, null); heartbeatAt = System.currentTimeMillis() + heartbeat; } items.close(); } if (Thread.currentThread().isInterrupted()) log.format("W: interrupt received, killing worker\n"); return null; } public void destroy() { ctx.destroy(); } // ============== getters and setters ================= public int getHeartbeat() { return heartbeat; } public void setHeartbeat(int heartbeat) { this.heartbeat = heartbeat; } public int getReconnect() { return reconnect; } public void setReconnect(int reconnect) { this.reconnect = reconnect; } } jeromq-0.6.0/src/test/java/guide/mmiecho.java000066400000000000000000000015401455771126300210750ustar00rootroot00000000000000package guide; import org.zeromq.ZMsg; /** * MMI echo query example */ public class mmiecho { public static void main(String[] args) { boolean verbose = (args.length > 0 && "-v".equals(args[0])); mdcliapi clientSession = new mdcliapi("tcp://localhost:5555", verbose); ZMsg request = new ZMsg(); // This is the service we want to look up request.addString("echo"); // This is the service we send our request to ZMsg reply = clientSession.send("mmi.service", request); if (reply != null) { String replyCode = reply.getFirst().toString(); System.out.printf("Lookup echo service: %s\n", replyCode); } else { System.out.println("E: no response from broker, make sure it's running"); } clientSession.destroy(); } } jeromq-0.6.0/src/test/java/guide/msgqueue.java000066400000000000000000000014311455771126300213060ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Simple message queuing broker * Same as request-reply broker but using QUEUE device. */ public class msgqueue { public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { // Socket facing clients Socket frontend = context.createSocket(SocketType.ROUTER); frontend.bind("tcp://*:5559"); // Socket facing services Socket backend = context.createSocket(SocketType.DEALER); backend.bind("tcp://*:5560"); // Start the proxy ZMQ.proxy(frontend, backend, null); } } } jeromq-0.6.0/src/test/java/guide/mspoller.java000066400000000000000000000026661455771126300213230ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Reading from multiple sockets in Java // This version uses ZMQ.Poller // public class mspoller { public static void main(String[] args) { try (ZContext context = new ZContext()) { // Connect to task ventilator ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.connect("tcp://localhost:5557"); // Connect to weather server ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5556"); subscriber.subscribe("10001 ".getBytes(ZMQ.CHARSET)); // Initialize poll set ZMQ.Poller items = context.createPoller(2); items.register(receiver, ZMQ.Poller.POLLIN); items.register(subscriber, ZMQ.Poller.POLLIN); // Process messages from both sockets while (!Thread.currentThread().isInterrupted()) { byte[] message; items.poll(); if (items.pollin(0)) { message = receiver.recv(0); System.out.println("Process task"); } if (items.pollin(1)) { message = subscriber.recv(0); System.out.println("Process weather update"); } } } } } jeromq-0.6.0/src/test/java/guide/msreader.java000066400000000000000000000030101455771126300212500ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Reading from multiple sockets in Java // This version uses a simple recv loop // public class msreader { public static void main(String[] args) throws Exception { // Prepare our context and sockets try (ZContext context = new ZContext()) { // Connect to task ventilator ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.connect("tcp://localhost:5557"); // Connect to weather server ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5556"); subscriber.subscribe("10001 ".getBytes(ZMQ.CHARSET)); // Process messages from both sockets // We prioritize traffic from the task ventilator while (!Thread.currentThread().isInterrupted()) { // Process any waiting tasks byte[] task; while ((task = receiver.recv(ZMQ.DONTWAIT)) != null) { System.out.println("process task"); } // Process any waiting weather updates byte[] update; while ((update = subscriber.recv(ZMQ.DONTWAIT)) != null) { System.out.println("process weather update"); } // No activity, so sleep for 1 msec Thread.sleep(1000); } } } } jeromq-0.6.0/src/test/java/guide/mtrelay.java000066400000000000000000000053621455771126300211370ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Socket; /** * Multithreaded relay */ public class mtrelay { private static class Step1 extends Thread { private final ZContext context; private Step1(ZContext context) { this.context = context; } @Override public void run() { // Signal downstream to step 2 Socket xmitter = context.createSocket(SocketType.PAIR); xmitter.connect("inproc://step2"); System.out.println("Step 1 ready, signaling step 2"); xmitter.send("READY", 0); xmitter.close(); } } private static class Step2 extends Thread { private final ZContext context; private Step2(ZContext context) { this.context = context; } @Override public void run() { // Bind to inproc: endpoint, then start upstream thread Socket receiver = context.createSocket(SocketType.PAIR); receiver.bind("inproc://step2"); // Wait for signal receiver.recv(0); receiver.close(); // Connect to step3 and tell it we're ready Socket xmitter = context.createSocket(SocketType.PAIR); xmitter.connect("inproc://step3"); System.out.println("Step 2 ready, signaling step 3"); xmitter.send("READY", 0); xmitter.close(); } } private static class Step3 extends Thread { private final ZContext context; private Step3(ZContext context) { this.context = context; } @Override public void run() { // Bind to inproc: endpoint, then start upstream thread Socket receiver = context.createSocket(SocketType.PAIR); receiver.bind("inproc://step3"); // Wait for signal receiver.recv(0); receiver.close(); System.out.println("Step 3 ready"); } } public static void main(String[] args) throws InterruptedException { try (ZContext context = new ZContext()) { // Step 1 signals to step 2 Thread step1 = new Step1(context); step1.start(); // Step 2 relays the signal from step 1 to step 3 Thread step2 = new Step2(context); step2.start(); // Step 3 waits for signal from step 2 Thread step3 = new Step3(context); step3.start(); step1.join(); step2.join(); step3.join(); System.out.println("Test successful!"); } } } jeromq-0.6.0/src/test/java/guide/mtserver.java000066400000000000000000000033421455771126300213250ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Multi threaded Hello World server */ public class mtserver { private static class Worker extends Thread { private final ZContext context; private Worker(ZContext context) { this.context = context; } @Override public void run() { ZMQ.Socket socket = context.createSocket(SocketType.REP); socket.connect("inproc://workers"); while (true) { // Wait for next request from client (C string) String request = socket.recvStr(0); System.out.println(Thread.currentThread().getName() + " Received request: [" + request + "]"); // Do some 'work' try { Thread.sleep(1000); } catch (InterruptedException e) { } // Send reply back to client (C string) socket.send("world", 0); } } } public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket clients = context.createSocket(SocketType.ROUTER); clients.bind("tcp://*:5555"); Socket workers = context.createSocket(SocketType.DEALER); workers.bind("inproc://workers"); for (int thread_nbr = 0; thread_nbr < 5; thread_nbr++) { Thread worker = new Worker(context); worker.start(); } // Connect work threads to client threads via a queue ZMQ.proxy(clients, workers, null); } } } jeromq-0.6.0/src/test/java/guide/pathopub.java000066400000000000000000000025501455771126300213000ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; // Pathological publisher // Sends out 1,000 topics and then one random update per second public class pathopub { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { Socket publisher = context.createSocket(SocketType.PUB); if (args.length == 1) publisher.connect(args[0]); else publisher.bind("tcp://*:5556"); // Ensure subscriber connection has time to complete Thread.sleep(1000); // Send out all 1,000 topic messages int topicNbr; for (topicNbr = 0; topicNbr < 1000; topicNbr++) { publisher.send(String.format("%03d", topicNbr), ZMQ.SNDMORE); publisher.send("Save Roger"); } // Send one random update per second Random rand = new Random(System.currentTimeMillis()); while (!Thread.currentThread().isInterrupted()) { Thread.sleep(1000); publisher.send( String.format("%03d", rand.nextInt(1000)), ZMQ.SNDMORE ); publisher.send("Off with his head!"); } } } } jeromq-0.6.0/src/test/java/guide/pathosub.java000066400000000000000000000021331455771126300213000ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; // Pathological subscriber // Subscribes to one random topic and prints received messages public class pathosub { public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket subscriber = context.createSocket(SocketType.SUB); if (args.length == 1) subscriber.connect(args[0]); else subscriber.connect("tcp://localhost:5556"); Random rand = new Random(System.currentTimeMillis()); String subscription = String.format("%03d", rand.nextInt(1000)); subscriber.subscribe(subscription.getBytes(ZMQ.CHARSET)); while (true) { String topic = subscriber.recvStr(); if (topic == null) break; String data = subscriber.recvStr(); assert (topic.equals(subscription)); System.out.println(data); } } } } jeromq-0.6.0/src/test/java/guide/peering1.java000066400000000000000000000050671455771126300211760ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // Broker peering simulation (part 1) // Prototypes the state flow public class peering1 { public static void main(String[] argv) { // First argument is this broker's name // Other arguments are our peers' names // if (argv.length < 1) { System.out.println("syntax: peering1 me {you}\n"); System.exit(-1); } String self = argv[0]; System.out.printf("I: preparing broker at %s\n%n", self); Random rand = new Random(System.nanoTime()); try (ZContext ctx = new ZContext()) { // Bind state backend to endpoint Socket statebe = ctx.createSocket(SocketType.PUB); statebe.bind(String.format("ipc://%s-state.ipc", self)); // Connect statefe to all peers Socket statefe = ctx.createSocket(SocketType.SUB); statefe.subscribe(ZMQ.SUBSCRIPTION_ALL); int argn; for (argn = 1; argn < argv.length; argn++) { String peer = argv[argn]; System.out.printf( "I: connecting to state backend at '%s'\n", peer ); statefe.connect(String.format("ipc://%s-state.ipc", peer)); } // The main loop sends out status messages to peers, and collects // status messages back from peers. The zmq_poll timeout defines // our own heartbeat. Poller poller = ctx.createPoller(1); poller.register(statefe, Poller.POLLIN); while (true) { // Poll for activity, or 1 second timeout int rc = poller.poll(1000); if (rc == -1) break; // Interrupted // Handle incoming status messages if (poller.pollin(0)) { String peer_name = new String(statefe.recv(0), ZMQ.CHARSET); String available = new String(statefe.recv(0), ZMQ.CHARSET); System.out.printf( "%s - %s workers free\n", peer_name, available ); } else { // Send random values for worker availability statebe.send(self, ZMQ.SNDMORE); statebe.send(String.format("%d", rand.nextInt(10)), 0); } } } } } jeromq-0.6.0/src/test/java/guide/peering2.java000066400000000000000000000224521455771126300211740ustar00rootroot00000000000000package guide; import java.io.IOException; import java.util.ArrayList; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // Broker peering simulation (part 2) // Prototypes the request-reply flow public class peering2 { private static final int NBR_CLIENTS = 10; private static final int NBR_WORKERS = 3; private static final String WORKER_READY = "\001"; // Signals worker is ready // Our own name; in practice this would be configured per node private static String self; // The client task does a request-reply dialog using a standard // synchronous REQ socket: private static class client_task extends Thread { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket client = ctx.createSocket(SocketType.REQ); client.connect(String.format("ipc://%s-localfe.ipc", self)); while (true) { // Send request, get reply client.send("HELLO", 0); String reply = client.recvStr(0); if (reply == null) break; // Interrupted System.out.printf("Client: %s\n", reply); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } } } // The worker task plugs into the LRU routing dialog using a REQ // socket: private static class worker_task extends Thread { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket worker = ctx.createSocket(SocketType.REQ); worker.connect(String.format("ipc://%s-localbe.ipc", self)); // Tell broker we're ready for work ZFrame frame = new ZFrame(WORKER_READY); frame.send(worker, 0); while (true) { // Send request, get reply ZMsg msg = ZMsg.recvMsg(worker, 0); if (msg == null) break; // Interrupted msg.getLast().print("Worker: "); msg.getLast().reset("OK"); msg.send(worker); } } } } // The main task begins by setting-up its frontend and backend sockets // and then starting its client and worker tasks: public static void main(String[] argv) { // First argument is this broker's name // Other arguments are our peers' names // if (argv.length < 1) { System.out.println("syntax: peering2 me {you}"); System.exit(-1); } self = argv[0]; System.out.printf("I: preparing broker at %s\n", self); Random rand = new Random(System.nanoTime()); try (ZContext ctx = new ZContext()) { // Bind cloud frontend to endpoint Socket cloudfe = ctx.createSocket(SocketType.ROUTER); cloudfe.setIdentity(self.getBytes(ZMQ.CHARSET)); cloudfe.bind(String.format("ipc://%s-cloud.ipc", self)); // Connect cloud backend to all peers Socket cloudbe = ctx.createSocket(SocketType.ROUTER); cloudbe.setIdentity(self.getBytes(ZMQ.CHARSET)); int argn; for (argn = 1; argn < argv.length; argn++) { String peer = argv[argn]; System.out.printf( "I: connecting to cloud forintend at '%s'\n", peer ); cloudbe.connect(String.format("ipc://%s-cloud.ipc", peer)); } // Prepare local frontend and backend Socket localfe = ctx.createSocket(SocketType.ROUTER); localfe.bind(String.format("ipc://%s-localfe.ipc", self)); Socket localbe = ctx.createSocket(SocketType.ROUTER); localbe.bind(String.format("ipc://%s-localbe.ipc", self)); // Get user to tell us when we can start System.out.println("Press Enter when all brokers are started: "); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } // Start local workers int worker_nbr; for (worker_nbr = 0; worker_nbr < NBR_WORKERS; worker_nbr++) new worker_task().start(); // Start local clients int client_nbr; for (client_nbr = 0; client_nbr < NBR_CLIENTS; client_nbr++) new client_task().start(); // Here we handle the request-reply flow. We're using the LRU // approach to poll workers at all times, and clients only when // there are one or more workers available. // Least recently used queue of available workers int capacity = 0; ArrayList workers = new ArrayList<>(); Poller backends = ctx.createPoller(2); backends.register(localbe, Poller.POLLIN); backends.register(cloudbe, Poller.POLLIN); Poller frontends = ctx.createPoller(2); frontends.register(localfe, Poller.POLLIN); frontends.register(cloudfe, Poller.POLLIN); while (true) { // First, route any waiting replies from workers // If we have no workers anyhow, wait indefinitely int rc = backends.poll(capacity > 0 ? 1000 : -1); if (rc == -1) break; // Interrupted // Handle reply from local worker ZMsg msg = null; if (backends.pollin(0)) { msg = ZMsg.recvMsg(localbe); if (msg == null) break; // Interrupted ZFrame address = msg.unwrap(); workers.add(address); capacity++; // If it's READY, don't route the message any further ZFrame frame = msg.getFirst(); String frameData = new String(frame.getData(), ZMQ.CHARSET); if (frameData.equals(WORKER_READY)) { msg.destroy(); msg = null; } } // Or handle reply from peer broker else if (backends.pollin(1)) { msg = ZMsg.recvMsg(cloudbe); if (msg == null) break; // Interrupted // We don't use peer broker address for anything ZFrame address = msg.unwrap(); address.destroy(); } // Route reply to cloud if it's addressed to a broker for (argn = 1; msg != null && argn < argv.length; argn++) { byte[] data = msg.getFirst().getData(); if (argv[argn].equals(new String(data, ZMQ.CHARSET))) { msg.send(cloudfe); msg = null; } } // Route reply to client if we still need to if (msg != null) msg.send(localfe); // Now we route as many client requests as we have worker // capacity for. We may reroute requests from our local // frontend, but not from // the cloud frontend. We reroute // randomly now, just to test things out. In the next version // we'll do this properly by calculating cloud capacity:// while (capacity > 0) { rc = frontends.poll(0); assert (rc >= 0); int reroutable; // We'll do peer brokers first, to prevent starvation if (frontends.pollin(1)) { msg = ZMsg.recvMsg(cloudfe); reroutable = 0; } else if (frontends.pollin(0)) { msg = ZMsg.recvMsg(localfe); reroutable = 1; } else break; // No work, go back to backends // If reroutable, send to cloud 20% of the time // Here we'd normally use cloud status information if (reroutable != 0 && argv.length > 1 && rand.nextInt(5) == 0) { // Route to random broker peer int random_peer = rand.nextInt(argv.length - 1) + 1; msg.push(argv[random_peer]); msg.send(cloudbe); } else { ZFrame frame = workers.remove(0); msg.wrap(frame); msg.send(localbe); capacity--; } } } // When we're done, clean up properly while (!workers.isEmpty()) { ZFrame frame = workers.remove(0); frame.destroy(); } } } } jeromq-0.6.0/src/test/java/guide/peering3.java000066400000000000000000000321301455771126300211670ustar00rootroot00000000000000package guide; import java.util.ArrayList; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // Broker peering simulation (part 3) // Prototypes the full flow of status and tasks public class peering3 { private static final int NBR_CLIENTS = 10; private static final int NBR_WORKERS = 5; // Signals worker is ready private static final String WORKER_READY = "\001"; // Our own name; in practice this would be configured per node private static String self; // This is the client task. It issues a burst of requests and then sleeps // for a few seconds. This simulates sporadic activity; when a number of // clients are active at once, the local workers should be overloaded. The // client uses a REQ socket for requests and also pushes statistics to the // monitor socket: private static class client_task extends Thread { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket client = ctx.createSocket(SocketType.REQ); client.connect(String.format("ipc://%s-localfe.ipc", self)); Socket monitor = ctx.createSocket(SocketType.PUSH); monitor.connect(String.format("ipc://%s-monitor.ipc", self)); Random rand = new Random(System.nanoTime()); Poller poller = ctx.createPoller(1); poller.register(client, Poller.POLLIN); boolean done = false; while (!done) { try { Thread.sleep(rand.nextInt(5) * 1000); } catch (InterruptedException e1) { } int burst = rand.nextInt(15); while (burst > 0) { String taskId = String.format( "%04X", rand.nextInt(10000) ); // Send request, get reply client.send(taskId, 0); // Wait max ten seconds for a reply, then complain int rc = poller.poll(10 * 1000); if (rc == -1) break; // Interrupted if (poller.pollin(0)) { String reply = client.recvStr(0); if (reply == null) break; // Interrupted // Worker is supposed to answer us with our task id assert (reply.equals(taskId)); monitor.send(String.format("%s", reply), 0); } else { monitor.send( String.format( "E: CLIENT EXIT - lost task %s", taskId ), 0); done = true; break; } burst--; } } } } } // This is the worker task, which uses a REQ socket to plug into the LRU // router. It's the same stub worker task you've seen in other examples: private static class worker_task extends Thread { @Override public void run() { Random rand = new Random(System.nanoTime()); try (ZContext ctx = new ZContext()) { Socket worker = ctx.createSocket(SocketType.REQ); worker.connect(String.format("ipc://%s-localbe.ipc", self)); // Tell broker we're ready for work ZFrame frame = new ZFrame(WORKER_READY); frame.send(worker, 0); while (true) { // Send request, get reply ZMsg msg = ZMsg.recvMsg(worker, 0); if (msg == null) break; // Interrupted // Workers are busy for 0/1 seconds try { Thread.sleep(rand.nextInt(2) * 1000); } catch (InterruptedException e) { } msg.send(worker); } } } } // The main task begins by setting-up all its sockets. The local frontend // talks to clients, and our local backend talks to workers. The cloud // frontend talks to peer brokers as if they were clients, and the cloud // backend talks to peer brokers as if they were workers. The state // backend publishes regular state messages, and the state frontend // subscribes to all state backends to collect these messages. Finally, // we use a PULL monitor socket to collect printable messages from tasks: public static void main(String[] argv) { // First argument is this broker's name // Other arguments are our peers' names // if (argv.length < 1) { System.out.println("syntax: peering3 me {you}"); System.exit(-1); } self = argv[0]; System.out.printf("I: preparing broker at %s\n", self); Random rand = new Random(System.nanoTime()); try (ZContext ctx = new ZContext()) { // Prepare local frontend and backend Socket localfe = ctx.createSocket(SocketType.ROUTER); localfe.bind(String.format("ipc://%s-localfe.ipc", self)); Socket localbe = ctx.createSocket(SocketType.ROUTER); localbe.bind(String.format("ipc://%s-localbe.ipc", self)); // Bind cloud frontend to endpoint Socket cloudfe = ctx.createSocket(SocketType.ROUTER); cloudfe.setIdentity(self.getBytes(ZMQ.CHARSET)); cloudfe.bind(String.format("ipc://%s-cloud.ipc", self)); // Connect cloud backend to all peers Socket cloudbe = ctx.createSocket(SocketType.ROUTER); cloudbe.setIdentity(self.getBytes(ZMQ.CHARSET)); int argn; for (argn = 1; argn < argv.length; argn++) { String peer = argv[argn]; System.out.printf( "I: connecting to cloud forintend at '%s'\n", peer ); cloudbe.connect(String.format("ipc://%s-cloud.ipc", peer)); } // Bind state backend to endpoint Socket statebe = ctx.createSocket(SocketType.PUB); statebe.bind(String.format("ipc://%s-state.ipc", self)); // Connect statefe to all peers Socket statefe = ctx.createSocket(SocketType.SUB); statefe.subscribe(ZMQ.SUBSCRIPTION_ALL); for (argn = 1; argn < argv.length; argn++) { String peer = argv[argn]; System.out.printf( "I: connecting to state backend at '%s'\n", peer ); statefe.connect(String.format("ipc://%s-state.ipc", peer)); } // Prepare monitor socket Socket monitor = ctx.createSocket(SocketType.PULL); monitor.bind(String.format("ipc://%s-monitor.ipc", self)); // Start local workers int worker_nbr; for (worker_nbr = 0; worker_nbr < NBR_WORKERS; worker_nbr++) new worker_task().start(); // Start local clients int client_nbr; for (client_nbr = 0; client_nbr < NBR_CLIENTS; client_nbr++) new client_task().start(); // Queue of available workers int localCapacity = 0; int cloudCapacity = 0; ArrayList workers = new ArrayList<>(); // The main loop has two parts. First we poll workers and our two // service sockets (statefe and monitor), in any case. If we have // no ready workers, there's no point in looking at incoming // requests. These can remain on their internal 0MQ queues: Poller primary = ctx.createPoller(4); primary.register(localbe, Poller.POLLIN); primary.register(cloudbe, Poller.POLLIN); primary.register(statefe, Poller.POLLIN); primary.register(monitor, Poller.POLLIN); Poller secondary = ctx.createPoller(2); secondary.register(localfe, Poller.POLLIN); secondary.register(cloudfe, Poller.POLLIN); while (true) { // First, route any waiting replies from workers // If we have no workers anyhow, wait indefinitely int rc = primary.poll(localCapacity > 0 ? 1000 : -1); if (rc == -1) break; // Interrupted // Track if capacity changes during this iteration int previous = localCapacity; // Handle reply from local worker ZMsg msg = null; if (primary.pollin(0)) { msg = ZMsg.recvMsg(localbe); if (msg == null) break; // Interrupted ZFrame address = msg.unwrap(); workers.add(address); localCapacity++; // If it's READY, don't route the message any further ZFrame frame = msg.getFirst(); String frameData = new String(frame.getData(), ZMQ.CHARSET); if (frameData.equals(WORKER_READY)) { msg.destroy(); msg = null; } } // Or handle reply from peer broker else if (primary.pollin(1)) { msg = ZMsg.recvMsg(cloudbe); if (msg == null) break; // Interrupted // We don't use peer broker address for anything ZFrame address = msg.unwrap(); address.destroy(); } // Route reply to cloud if it's addressed to a broker for (argn = 1; msg != null && argn < argv.length; argn++) { byte[] data = msg.getFirst().getData(); if (argv[argn].equals(new String(data, ZMQ.CHARSET))) { msg.send(cloudfe); msg = null; } } // Route reply to client if we still need to if (msg != null) msg.send(localfe); // If we have input messages on our statefe or monitor sockets // we can process these immediately: if (primary.pollin(2)) { String peer = statefe.recvStr(); String status = statefe.recvStr(); cloudCapacity = Integer.parseInt(status); } if (primary.pollin(3)) { String status = monitor.recvStr(); System.out.println(status); } // Now we route as many client requests as we have worker // capacity for. We may reroute requests from our local // frontend, but not from the cloud frontend. We reroute // randomly now, just to test things out. In the next version // we'll do this properly by calculating cloud capacity. while (localCapacity + cloudCapacity > 0) { rc = secondary.poll(0); assert (rc >= 0); if (secondary.pollin(0)) { msg = ZMsg.recvMsg(localfe); } else if (localCapacity > 0 && secondary.pollin(1)) { msg = ZMsg.recvMsg(cloudfe); } else break; // No work, go back to backends if (localCapacity > 0) { ZFrame frame = workers.remove(0); msg.wrap(frame); msg.send(localbe); localCapacity--; } else { // Route to random broker peer int random_peer = rand.nextInt(argv.length - 1) + 1; msg.push(argv[random_peer]); msg.send(cloudbe); } } // We broadcast capacity messages to other peers; to reduce // chatter we do this only if our capacity changed. if (localCapacity != previous) { // We stick our own address onto the envelope statebe.sendMore(self); // Broadcast new capacity statebe.send(String.format("%d", localCapacity), 0); } } // When we're done, clean up properly while (!workers.isEmpty()) { ZFrame frame = workers.remove(0); frame.destroy(); } } } } jeromq-0.6.0/src/test/java/guide/ppqueue.java000066400000000000000000000136141455771126300211450ustar00rootroot00000000000000package guide; import java.util.ArrayList; import java.util.Iterator; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // // Paranoid Pirate queue // public class ppqueue { private final static int HEARTBEAT_LIVENESS = 3; // 3-5 is reasonable private final static int HEARTBEAT_INTERVAL = 1000; // msecs // Paranoid Pirate Protocol constants private final static String PPP_READY = "\001"; // Signals worker is ready private final static String PPP_HEARTBEAT = "\002"; // Signals worker heartbeat // Here we define the worker class; a structure and a set of functions that // as constructor, destructor, and methods on worker objects: private static class Worker { final ZFrame address; // Address of worker final String identity; // Printable identity final long expiry; // Expires at this time protected Worker(ZFrame address) { this.address = address; identity = new String(address.getData(), ZMQ.CHARSET); expiry = System.currentTimeMillis() + HEARTBEAT_INTERVAL * HEARTBEAT_LIVENESS; } // The ready method puts a worker to the end of the ready list: protected void ready(ArrayList workers) { Iterator it = workers.iterator(); while (it.hasNext()) { Worker worker = it.next(); if (identity.equals(worker.identity)) { it.remove(); break; } } workers.add(this); } // The next method returns the next available worker address: protected static ZFrame next(ArrayList workers) { Worker worker = workers.remove(0); assert (worker != null); return worker.address; } // The purge method looks for and kills expired workers. We hold workers // from oldest to most recent, so we stop at the first alive worker: protected static void purge(ArrayList workers) { Iterator it = workers.iterator(); while (it.hasNext()) { Worker worker = it.next(); if (System.currentTimeMillis() < worker.expiry) { break; } it.remove(); } } } // The main task is an LRU queue with heartbeating on workers so we can // detect crashed or blocked worker tasks: public static void main(String[] args) { try (ZContext ctx = new ZContext()) { Socket frontend = ctx.createSocket(SocketType.ROUTER); Socket backend = ctx.createSocket(SocketType.ROUTER); frontend.bind("tcp://*:5555"); // For clients backend.bind("tcp://*:5556"); // For workers // List of available workers ArrayList workers = new ArrayList<>(); // Send out heartbeats at regular intervals long heartbeat_at = System.currentTimeMillis() + HEARTBEAT_INTERVAL; Poller poller = ctx.createPoller(2); poller.register(backend, Poller.POLLIN); poller.register(frontend, Poller.POLLIN); while (true) { boolean workersAvailable = !workers.isEmpty(); int rc = poller.poll(HEARTBEAT_INTERVAL); if (rc == -1) break; // Interrupted // Handle worker activity on backend if (poller.pollin(0)) { // Use worker address for LRU routing ZMsg msg = ZMsg.recvMsg(backend); if (msg == null) break; // Interrupted // Any sign of life from worker means it's ready ZFrame address = msg.unwrap(); Worker worker = new Worker(address); worker.ready(workers); // Validate control message, or return reply to client if (msg.size() == 1) { ZFrame frame = msg.getFirst(); String data = new String(frame.getData(), ZMQ.CHARSET); if (!data.equals(PPP_READY) && !data.equals(PPP_HEARTBEAT)) { System.out.println( "E: invalid message from worker" ); msg.dump(System.out); } msg.destroy(); } else msg.send(frontend); } if (workersAvailable && poller.pollin(1)) { // Now get next client request, route to next worker ZMsg msg = ZMsg.recvMsg(frontend); if (msg == null) break; // Interrupted msg.push(Worker.next(workers)); msg.send(backend); } // We handle heartbeating after any socket activity. First we // send heartbeats to any idle workers if it's time. Then we // purge any dead workers: if (System.currentTimeMillis() >= heartbeat_at) { for (Worker worker : workers) { worker.address.send( backend, ZFrame.REUSE + ZFrame.MORE ); ZFrame frame = new ZFrame(PPP_HEARTBEAT); frame.send(backend, 0); } long now = System.currentTimeMillis(); heartbeat_at = now + HEARTBEAT_INTERVAL; } Worker.purge(workers); } // When we're done, clean up properly workers.clear(); } } } jeromq-0.6.0/src/test/java/guide/ppworker.java000066400000000000000000000152151455771126300213310ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // // Paranoid Pirate worker // public class ppworker { private final static int HEARTBEAT_LIVENESS = 3; // 3-5 is reasonable private final static int HEARTBEAT_INTERVAL = 1000; // msecs private final static int INTERVAL_INIT = 1000; // Initial reconnect private final static int INTERVAL_MAX = 32000; // After exponential backoff // Paranoid Pirate Protocol constants private final static String PPP_READY = "\001"; // Signals worker is ready private final static String PPP_HEARTBEAT = "\002"; // Signals worker heartbeat // Helper function that returns a new configured socket // connected to the Paranoid Pirate queue private static Socket worker_socket(ZContext ctx) { Socket worker = ctx.createSocket(SocketType.DEALER); worker.connect("tcp://localhost:5556"); // Tell queue we're ready for work System.out.println("I: worker ready\n"); ZFrame frame = new ZFrame(PPP_READY); frame.send(worker, 0); return worker; } // We have a single task, which implements the worker side of the // Paranoid Pirate Protocol (PPP). The interesting parts here are // the heartbeating, which lets the worker detect if the queue has // died, and vice-versa: public static void main(String[] args) { try (ZContext ctx = new ZContext()) { Socket worker = worker_socket(ctx); Poller poller = ctx.createPoller(1); poller.register(worker, Poller.POLLIN); // If liveness hits zero, queue is considered disconnected int liveness = HEARTBEAT_LIVENESS; int interval = INTERVAL_INIT; // Send out heartbeats at regular intervals long heartbeat_at = System.currentTimeMillis() + HEARTBEAT_INTERVAL; Random rand = new Random(System.nanoTime()); int cycles = 0; while (true) { int rc = poller.poll(HEARTBEAT_INTERVAL); if (rc == -1) break; // Interrupted if (poller.pollin(0)) { // Get message // - 3-part envelope + content -> request // - 1-part HEARTBEAT -> heartbeat ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; // Interrupted // To test the robustness of the queue implementation we // simulate various typical problems, such as the worker // crashing, or running very slowly. We do this after a few // cycles so that the architecture can get up and running // first: if (msg.size() == 3) { cycles++; if (cycles > 3 && rand.nextInt(5) == 0) { System.out.println("I: simulating a crash\n"); msg.destroy(); break; } else if (cycles > 3 && rand.nextInt(5) == 0) { System.out.println("I: simulating CPU overload\n"); try { Thread.sleep(3000); } catch (InterruptedException e) { break; } } System.out.println("I: normal reply\n"); msg.send(worker); liveness = HEARTBEAT_LIVENESS; try { Thread.sleep(1000); } catch (InterruptedException e) { break; } // Do some heavy work } else // When we get a heartbeat message from the queue, it // means the queue was (recently) alive, so reset our // liveness indicator: if (msg.size() == 1) { ZFrame frame = msg.getFirst(); String frameData = new String( frame.getData(), ZMQ.CHARSET ); if (PPP_HEARTBEAT.equals(frameData)) liveness = HEARTBEAT_LIVENESS; else { System.out.println("E: invalid message\n"); msg.dump(System.out); } msg.destroy(); } else { System.out.println("E: invalid message\n"); msg.dump(System.out); } interval = INTERVAL_INIT; } else // If the queue hasn't sent us heartbeats in a while, // destroy the socket and reconnect. This is the simplest // most brutal way of discarding any messages we might have // sent in the meantime. if (--liveness == 0) { System.out.println( "W: heartbeat failure, can't reach queue\n" ); System.out.printf( "W: reconnecting in %sd msec\n", interval ); try { Thread.sleep(interval); } catch (InterruptedException e) { e.printStackTrace(); } if (interval < INTERVAL_MAX) interval *= 2; worker.close(); worker = worker_socket(ctx); liveness = HEARTBEAT_LIVENESS; } // Send heartbeat to queue if it's time if (System.currentTimeMillis() > heartbeat_at) { long now = System.currentTimeMillis(); heartbeat_at = now + HEARTBEAT_INTERVAL; System.out.println("I: worker heartbeat\n"); ZFrame frame = new ZFrame(PPP_HEARTBEAT); frame.send(worker, 0); } } } } } jeromq-0.6.0/src/test/java/guide/psenvpub.java000066400000000000000000000014351455771126300213210ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Pubsub envelope publisher */ public class psenvpub { public static void main(String[] args) { // Prepare our context and publisher try (ZContext context = new ZContext()) { Socket publisher = context.createSocket(SocketType.PUB); publisher.bind("tcp://*:5563"); while (!Thread.currentThread().isInterrupted()) { // Write two messages, each with an envelope and content publisher.sendMore("A"); publisher.send("We don't want to see this"); publisher.sendMore("B"); publisher.send("We would like to see this"); } } } } jeromq-0.6.0/src/test/java/guide/psenvsub.java000066400000000000000000000015611455771126300213240ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Pubsub envelope subscriber */ public class psenvsub { public static void main(String[] args) { // Prepare our context and subscriber try (ZContext context = new ZContext()) { Socket subscriber = context.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5563"); subscriber.subscribe("B".getBytes(ZMQ.CHARSET)); while (!Thread.currentThread().isInterrupted()) { // Read envelope with address String address = subscriber.recvStr(); // Read message contents String contents = subscriber.recvStr(); System.out.println(address + " : " + contents); } } } } jeromq-0.6.0/src/test/java/guide/rrbroker.java000066400000000000000000000040421455771126300213040ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Simple request-reply broker * */ public class rrbroker { public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { Socket frontend = context.createSocket(SocketType.ROUTER); Socket backend = context.createSocket(SocketType.DEALER); frontend.bind("tcp://*:5559"); backend.bind("tcp://*:5560"); System.out.println("launch and connect broker."); // Initialize poll set Poller items = context.createPoller(2); items.register(frontend, Poller.POLLIN); items.register(backend, Poller.POLLIN); boolean more; byte[] message; // Switch messages between sockets while (!Thread.currentThread().isInterrupted()) { // poll and memorize multipart detection items.poll(); if (items.pollin(0)) { while (true) { // receive message message = frontend.recv(0); more = frontend.hasReceiveMore(); // Broker it backend.send(message, more ? ZMQ.SNDMORE : 0); if (!more) { break; } } } if (items.pollin(1)) { while (true) { // receive message message = backend.recv(0); more = backend.hasReceiveMore(); // Broker it frontend.send(message, more ? ZMQ.SNDMORE : 0); if (!more) { break; } } } } } } } jeromq-0.6.0/src/test/java/guide/rrclient.java000066400000000000000000000016161455771126300213020ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Hello World client * Connects REQ socket to tcp://localhost:5559 * Sends "Hello" to server, expects "World" back */ public class rrclient { public static void main(String[] args) { try (ZContext context = new ZContext()) { // Socket to talk to server Socket requester = context.createSocket(SocketType.REQ); requester.connect("tcp://localhost:5559"); System.out.println("launch and connect client."); for (int request_nbr = 0; request_nbr < 10; request_nbr++) { requester.send("Hello", 0); String reply = requester.recvStr(0); System.out.println( "Received reply " + request_nbr + " [" + reply + "]" ); } } } } jeromq-0.6.0/src/test/java/guide/rrworker.java000066400000000000000000000017011455771126300213300ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; // Hello World worker // Connects REP socket to tcp://*:5560 // Expects "Hello" from client, replies with "World" public class rrworker { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { // Socket to talk to server Socket responder = context.createSocket(SocketType.REP); responder.connect("tcp://localhost:5560"); while (!Thread.currentThread().isInterrupted()) { // Wait for next request from client String string = responder.recvStr(0); System.out.printf("Received request: [%s]\n", string); // Do some 'work' Thread.sleep(1000); // Send reply back to client responder.send("World"); } } } } jeromq-0.6.0/src/test/java/guide/rtdealer.java000066400000000000000000000057711455771126300212700ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * ROUTER-TO-REQ example */ public class rtdealer { private static final Random rand = new Random(); private static final int NBR_WORKERS = 10; private static class Worker extends Thread { @Override public void run() { try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.DEALER); ZHelper.setId(worker); // Set a printable identity worker.connect("tcp://localhost:5671"); int total = 0; while (true) { // Tell the broker we're ready for work worker.sendMore(""); worker.send("Hi Boss"); // Get workload from broker, until finished worker.recvStr(); // Envelope delimiter String workload = worker.recvStr(); boolean finished = workload.equals("Fired!"); if (finished) { System.out.printf("Completed: %d tasks\n", total); break; } total++; // Do some random work try { Thread.sleep(rand.nextInt(500) + 1); } catch (InterruptedException e) { } } } } } /** * While this example runs in a single process, that is just to make * it easier to start and stop the example. Each thread has its own * context and conceptually acts as a separate process. */ public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket broker = context.createSocket(SocketType.ROUTER); broker.bind("tcp://*:5671"); for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) { Thread worker = new Worker(); worker.start(); } // Run for five seconds and then tell workers to end long endTime = System.currentTimeMillis() + 5000; int workersFired = 0; while (true) { // Next message gives us least recently used worker String identity = broker.recvStr(); broker.sendMore(identity); broker.recv(0); // Envelope delimiter broker.recv(0); // Response from worker broker.sendMore(""); // Encourage workers until it's time to fire them if (System.currentTimeMillis() < endTime) broker.send("Work harder"); else { broker.send("Fired!"); if (++workersFired == NBR_WORKERS) break; } } } } } jeromq-0.6.0/src/test/java/guide/rtmama.java000066400000000000000000000043321455771126300207370ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // //Custom routing Router to Mama (ROUTER to REQ) // public class rtmama { private static final int NBR_WORKERS = 10; public static class Worker implements Runnable { private final byte[] END = "END".getBytes(ZMQ.CHARSET); public void run() { try (ZContext context = new ZContext()) { ZMQ.Socket worker = context.createSocket(SocketType.REQ); // worker.setIdentity(); will set a random id automatically worker.connect("ipc://routing.ipc"); int total = 0; while (true) { worker.send("ready", 0); byte[] workerload = worker.recv(0); if (new String(workerload, ZMQ.CHARSET).equals("END")) { System.out.printf("Processs %d tasks.%n", total); break; } total += 1; } } } } public static void main(String[] args) { try (ZContext context = new ZContext()) { ZMQ.Socket client = context.createSocket(SocketType.ROUTER); client.bind("ipc://routing.ipc"); for (int i = 0; i != NBR_WORKERS; i++) { new Thread(new Worker()).start(); } for (int i = 0; i != NBR_WORKERS; i++) { // LRU worker is next waiting in queue byte[] address = client.recv(0); byte[] empty = client.recv(0); byte[] ready = client.recv(0); client.send(address, ZMQ.SNDMORE); client.send("", ZMQ.SNDMORE); client.send("This is the workload", 0); } for (int i = 0; i != NBR_WORKERS; i++) { // LRU worker is next waiting in queue byte[] address = client.recv(0); byte[] empty = client.recv(0); byte[] ready = client.recv(0); client.send(address, ZMQ.SNDMORE); client.send("", ZMQ.SNDMORE); client.send("END", 0); } } } } jeromq-0.6.0/src/test/java/guide/rtpapa.java000066400000000000000000000032001455771126300207360ustar00rootroot00000000000000package guide; // //Custom routing Router to Papa (ROUTER to REP) // import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; public class rtpapa { // We will do this all in one thread to emphasize the getSequence of events public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket client = context.createSocket(SocketType.ROUTER); client.bind("ipc://routing.ipc"); Socket worker = context.createSocket(SocketType.REP); worker.setIdentity("A".getBytes(ZMQ.CHARSET)); worker.connect("ipc://routing.ipc"); // Wait for the worker to connect so that when we send a message // with routing envelope, it will actually match the worker try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // Send papa address, address stack, empty part, and request client.send("A", ZMQ.SNDMORE); client.send("address 3", ZMQ.SNDMORE); client.send("address 2", ZMQ.SNDMORE); client.send("address 1", ZMQ.SNDMORE); client.send("", ZMQ.SNDMORE); client.send("This is the workload", 0); // Worker should get just the workload ZHelper.dump(worker); // We don't play with envelopes in the worker worker.send("This is the reply", 0); // Now dump what we got off the ROUTER socket ZHelper.dump(client); } } } jeromq-0.6.0/src/test/java/guide/rtreq.java000066400000000000000000000056211455771126300206150ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * ROUTER-TO-REQ example */ public class rtreq { private static final Random rand = new Random(); private static final int NBR_WORKERS = 10; private static class Worker extends Thread { @Override public void run() { try (ZContext context = new ZContext()) { Socket worker = context.createSocket(SocketType.REQ); ZHelper.setId(worker); // Set a printable identity worker.connect("tcp://localhost:5671"); int total = 0; while (true) { // Tell the broker we're ready for work worker.send("Hi Boss"); // Get workload from broker, until finished String workload = worker.recvStr(); boolean finished = workload.equals("Fired!"); if (finished) { System.out.printf("Completed: %d tasks\n", total); break; } total++; // Do some random work try { Thread.sleep(rand.nextInt(500) + 1); } catch (InterruptedException e) { } } } } } /** * While this example runs in a single process, that is just to make * it easier to start and stop the example. Each thread has its own * context and conceptually acts as a separate process. */ public static void main(String[] args) { try (ZContext context = new ZContext()) { Socket broker = context.createSocket(SocketType.ROUTER); broker.bind("tcp://*:5671"); for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) { Thread worker = new Worker(); worker.start(); } // Run for five seconds and then tell workers to end long endTime = System.currentTimeMillis() + 5000; int workersFired = 0; while (true) { // Next message gives us least recently used worker String identity = broker.recvStr(); broker.sendMore(identity); broker.recvStr(); // Envelope delimiter broker.recvStr(); // Response from worker broker.sendMore(""); // Encourage workers until it's time to fire them if (System.currentTimeMillis() < endTime) broker.send("Work harder"); else { broker.send("Fired!"); if (++workersFired == NBR_WORKERS) break; } } } } } jeromq-0.6.0/src/test/java/guide/spqueue.java000066400000000000000000000051651455771126300211520ustar00rootroot00000000000000package guide; import java.util.ArrayList; import org.zeromq.*; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; // // Simple Pirate queue // This is identical to load-balancing pattern, with no reliability mechanisms // at all. It depends on the client for recovery. Runs forever. // public class spqueue { private final static String WORKER_READY = "\001"; // Signals worker is ready public static void main(String[] args) { try (ZContext ctx = new ZContext()) { Socket frontend = ctx.createSocket(SocketType.ROUTER); Socket backend = ctx.createSocket(SocketType.ROUTER); frontend.bind("tcp://*:5555"); // For clients backend.bind("tcp://*:5556"); // For workers // Queue of available workers ArrayList workers = new ArrayList<>(); Poller poller = ctx.createPoller(2); poller.register(backend, Poller.POLLIN); poller.register(frontend, Poller.POLLIN); // The body of this example is exactly the same as lruqueue2. while (true) { boolean workersAvailable = !workers.isEmpty(); int rc = poller.poll(-1); // Poll frontend only if we have available workers if (rc == -1) break; // Interrupted // Handle worker activity on backend if (poller.pollin(0)) { // Use worker address for LRU routing ZMsg msg = ZMsg.recvMsg(backend); if (msg == null) break; // Interrupted ZFrame address = msg.unwrap(); workers.add(address); // Forward message to client if it's not a READY ZFrame frame = msg.getFirst(); if (new String(frame.getData(), ZMQ.CHARSET).equals(WORKER_READY)) msg.destroy(); else msg.send(frontend); } if (workersAvailable && poller.pollin(1)) { // Get client request, route to first available worker ZMsg msg = ZMsg.recvMsg(frontend); if (msg != null) { msg.wrap(workers.remove(0)); msg.send(backend); } } } // When we're done, clean up properly while (!workers.isEmpty()) { ZFrame frame = workers.remove(0); frame.destroy(); } workers.clear(); } } } jeromq-0.6.0/src/test/java/guide/spworker.java000066400000000000000000000037351455771126300213400ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.*; import org.zeromq.ZMQ.Socket; // // Simple Pirate worker // Connects REQ socket to tcp://*:5556 // Implements worker part of load-balancing queueing // public class spworker { private final static String WORKER_READY = "\001"; // Signals worker is ready public static void main(String[] args) throws Exception { try (ZContext ctx = new ZContext()) { Socket worker = ctx.createSocket(SocketType.REQ); // Set random identity to make tracing easier Random rand = new Random(System.nanoTime()); String identity = String.format( "%04X-%04X", rand.nextInt(0x10000), rand.nextInt(0x10000) ); worker.setIdentity(identity.getBytes(ZMQ.CHARSET)); worker.connect("tcp://localhost:5556"); // Tell broker we're ready for work System.out.printf("I: (%s) worker ready\n", identity); ZFrame frame = new ZFrame(WORKER_READY); frame.send(worker, 0); int cycles = 0; while (true) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) break; // Interrupted // Simulate various problems, after a few cycles cycles++; if (cycles > 3 && rand.nextInt(5) == 0) { System.out.printf("I: (%s) simulating a crash\n", identity); msg.destroy(); break; } else if (cycles > 3 && rand.nextInt(5) == 0) { System.out.printf( "I: (%s) simulating CPU overload\n", identity ); Thread.sleep(3000); } System.out.printf("I: (%s) normal reply\n", identity); Thread.sleep(1000); // Do some heavy work msg.send(worker); } } } } jeromq-0.6.0/src/test/java/guide/suisnail.java000066400000000000000000000064761455771126300213200ustar00rootroot00000000000000package guide; import java.util.Random; // Suicidal Snail import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZThread; import org.zeromq.ZThread.IAttachedRunnable; public class suisnail { private static final long MAX_ALLOWED_DELAY = 1000; // msecs private static final Random rand = new Random(System.currentTimeMillis()); // This is our subscriber. It connects to the publisher and subscribes to // everything. It sleeps for a short time between messages to simulate // doing too much work. If a message is more than one second late, it // croaks. private static class Subscriber implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { // Subscribe to everything Socket subscriber = ctx.createSocket(SocketType.SUB); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); subscriber.connect("tcp://localhost:5556"); // Get and process messages while (true) { String string = subscriber.recvStr(); System.out.printf("%s\n", string); long clock = Long.parseLong(string); // Suicide snail logic if (System.currentTimeMillis() - clock > MAX_ALLOWED_DELAY) { System.err.println( "E: subscriber cannot keep up, aborting" ); break; } // Work for 1 msec plus some random additional time try { Thread.sleep(1000 + rand.nextInt(2000)); } catch (InterruptedException e) { break; } } pipe.send("gone and died"); } } // .split publisher task // This is our publisher task. It publishes a time-stamped message to its // PUB socket every millisecond: private static class Publisher implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { // Prepare publisher Socket publisher = ctx.createSocket(SocketType.PUB); publisher.bind("tcp://*:5556"); while (true) { // Send current clock (msecs) to subscribers String string = String.format("%d", System.currentTimeMillis()); publisher.send(string); String signal = pipe.recvStr(ZMQ.DONTWAIT); if (signal != null) { break; } try { Thread.sleep(1); } catch (InterruptedException e) { } } } } // .split main task // The main task simply starts a client and a server, and then waits for // the client to signal that it has died: public static void main(String[] args) throws Exception { try (ZContext ctx = new ZContext()) { Socket pubpipe = ZThread.fork(ctx, new Publisher()); Socket subpipe = ZThread.fork(ctx, new Subscriber()); subpipe.recvStr(); pubpipe.send("break"); Thread.sleep(100); } } } jeromq-0.6.0/src/test/java/guide/syncpub.java000066400000000000000000000031621455771126300211410ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Synchronized publisher. */ public class syncpub { /** * We wait for 10 subscribers */ protected static final int SUBSCRIBERS_EXPECTED = 10; public static void main(String[] args) { try (ZContext context = new ZContext()) { // Socket to talk to clients Socket publisher = context.createSocket(SocketType.PUB); publisher.setLinger(5000); // In 0MQ 3.x pub socket could drop messages if sub can follow the // generation of pub messages publisher.setSndHWM(0); publisher.bind("tcp://*:5561"); // Socket to receive signals Socket syncservice = context.createSocket(SocketType.REP); syncservice.bind("tcp://*:5562"); System.out.println("Waiting for subscribers"); // Get synchronization from subscribers int subscribers = 0; while (subscribers < SUBSCRIBERS_EXPECTED) { // - wait for synchronization request syncservice.recv(0); // - send synchronization reply syncservice.send("", 0); subscribers++; } // Now broadcast exactly 1M updates followed by END System.out.println("Broadcasting messages"); int update_nbr; for (update_nbr = 0; update_nbr < 1000000; update_nbr++) { publisher.send("Rhubarb", 0); } publisher.send("END", 0); } } } jeromq-0.6.0/src/test/java/guide/syncsub.java000066400000000000000000000024111455771126300211400ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Synchronized subscriber. */ public class syncsub { public static void main(String[] args) { try (ZContext context = new ZContext()) { // First, connect our subscriber socket Socket subscriber = context.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5561"); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); // Second, synchronize with publisher Socket syncclient = context.createSocket(SocketType.REQ); syncclient.connect("tcp://localhost:5562"); // - send a synchronization request syncclient.send(ZMQ.MESSAGE_SEPARATOR, 0); // - wait for synchronization reply syncclient.recv(0); // Third, get our updates and report how many we got int update_nbr = 0; while (true) { String string = subscriber.recvStr(0); if (string.equals("END")) { break; } update_nbr++; } System.out.println("Received " + update_nbr + " updates."); } } } jeromq-0.6.0/src/test/java/guide/tasksink.java000066400000000000000000000026351455771126300213110ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Task sink in Java // Binds PULL socket to tcp://localhost:5558 // Collects results from workers via that socket // public class tasksink { public static void main(String[] args) { // Prepare our context and socket try (ZContext context = new ZContext()) { ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.bind("tcp://*:5558"); // Wait for start of batch String string = new String(receiver.recv(0), ZMQ.CHARSET); // Start our clock now long tstart = System.currentTimeMillis(); // Process 100 confirmations int task_nbr; int total_msec = 0; // Total calculated cost in msecs for (task_nbr = 0; task_nbr < 100; task_nbr++) { string = new String(receiver.recv(0), ZMQ.CHARSET).trim(); if ((task_nbr / 10) * 10 == task_nbr) { System.out.print(":"); } else { System.out.print("."); } } // Calculate and report duration of batch long tend = System.currentTimeMillis(); System.out.println( "\nTotal elapsed time: " + (tend - tstart) + " msec" ); } } } jeromq-0.6.0/src/test/java/guide/tasksink2.java000066400000000000000000000031341455771126300213660ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; /** * Task sink - design 2 * Adds pub-sub flow to send kill signal to workers */ public class tasksink2 { public static void main(String[] args) throws Exception { // Prepare our context and socket try (ZContext context = new ZContext()) { ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.bind("tcp://*:5558"); // Socket for worker control ZMQ.Socket controller = context.createSocket(SocketType.PUB); controller.bind("tcp://*:5559"); // Wait for start of batch receiver.recv(0); // Start our clock now long tstart = System.currentTimeMillis(); // Process 100 confirmations int task_nbr; for (task_nbr = 0; task_nbr < 100; task_nbr++) { receiver.recv(0); if ((task_nbr / 10) * 10 == task_nbr) { System.out.print(":"); } else { System.out.print("."); } System.out.flush(); } // Calculate and report duration of batch long tend = System.currentTimeMillis(); System.out.println( "Total elapsed time: " + (tend - tstart) + " msec" ); // Send the kill signal to the workers controller.send("KILL", 0); // Give it some time to deliver Thread.sleep(1); } } } jeromq-0.6.0/src/test/java/guide/taskvent.java000066400000000000000000000034001455771126300213100ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Task ventilator in Java // Binds PUSH socket to tcp://localhost:5557 // Sends batch of tasks to workers via that socket // public class taskvent { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { // Socket to send messages on ZMQ.Socket sender = context.createSocket(SocketType.PUSH); sender.bind("tcp://*:5557"); // Socket to send messages on ZMQ.Socket sink = context.createSocket(SocketType.PUSH); sink.connect("tcp://localhost:5558"); System.out.println("Press Enter when the workers are ready: "); System.in.read(); System.out.println("Sending tasks to workers\n"); // The first message is "0" and signals start of batch sink.send("0", 0); // Initialize random number generator Random srandom = new Random(System.currentTimeMillis()); // Send 100 tasks int task_nbr; int total_msec = 0; // Total expected cost in msecs for (task_nbr = 0; task_nbr < 100; task_nbr++) { int workload; // Random workload from 1 to 100msecs workload = srandom.nextInt(100) + 1; total_msec += workload; System.out.print(workload + "."); String string = String.format("%d", workload); sender.send(string, 0); } System.out.println("Total expected cost: " + total_msec + " msec"); Thread.sleep(1000); // Give 0MQ time to deliver } } } jeromq-0.6.0/src/test/java/guide/taskwork.java000066400000000000000000000025451455771126300213270ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Task worker in Java // Connects PULL socket to tcp://localhost:5557 // Collects workloads from ventilator via that socket // Connects PUSH socket to tcp://localhost:5558 // Sends results to sink via that socket // public class taskwork { public static void main(String[] args) throws Exception { try (ZContext context = new ZContext()) { // Socket to receive messages on ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.connect("tcp://localhost:5557"); // Socket to send messages to ZMQ.Socket sender = context.createSocket(SocketType.PUSH); sender.connect("tcp://localhost:5558"); // Process tasks forever while (!Thread.currentThread().isInterrupted()) { String string = new String(receiver.recv(0), ZMQ.CHARSET).trim(); long msec = Long.parseLong(string); // Simple progress indicator for the viewer System.out.flush(); System.out.print(string + '.'); // Do the work Thread.sleep(msec); // Send results to sink sender.send(ZMQ.MESSAGE_SEPARATOR, 0); } } } } jeromq-0.6.0/src/test/java/guide/taskwork2.java000066400000000000000000000032521455771126300214050ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; /** * Task worker - design 2 * Adds pub-sub flow to receive and respond to kill signal */ public class taskwork2 { public static void main(String[] args) throws InterruptedException { try (ZContext context = new ZContext()) { ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.connect("tcp://localhost:5557"); ZMQ.Socket sender = context.createSocket(SocketType.PUSH); sender.connect("tcp://localhost:5558"); ZMQ.Socket controller = context.createSocket(SocketType.SUB); controller.connect("tcp://localhost:5559"); controller.subscribe(ZMQ.SUBSCRIPTION_ALL); ZMQ.Poller items = context.createPoller(2); items.register(receiver, ZMQ.Poller.POLLIN); items.register(controller, ZMQ.Poller.POLLIN); while (true) { items.poll(); if (items.pollin(0)) { String message = receiver.recvStr(0); long nsec = Long.parseLong(message); // Simple progress indicator for the viewer System.out.print(message + '.'); System.out.flush(); // Do the work Thread.sleep(nsec); // Send results to sink sender.send("", 0); } // Any waiting controller command acts as 'KILL' if (items.pollin(1)) { break; // Exit loop } } } } } jeromq-0.6.0/src/test/java/guide/ticlient.java000066400000000000000000000047431455771126300212770ustar00rootroot00000000000000package guide; // Titanic client example // Implements client side of http://rfc.zeromq.org/spec:9 // Calls a TSP service // Returns response if successful (status code 200 OK), else NULL // import org.zeromq.ZFrame; import org.zeromq.ZMsg; public class ticlient { static ZMsg serviceCall(mdcliapi session, String service, ZMsg request) { ZMsg reply = session.send(service, request); if (reply != null) { ZFrame status = reply.pop(); if (status.streq("200")) { status.destroy(); return reply; } else if (status.streq("400")) { System.out.println("E: client fatal error, aborting"); } else if (status.streq("500")) { System.out.println("E: server fatal error, aborting"); } reply.destroy(); } return null; // Didn't succeed; don't care why not } public static void main(String[] args) throws Exception { boolean verbose = (args.length > 0 && args[0].equals("-v")); mdcliapi session = new mdcliapi("tcp://localhost:5555", verbose); // 1. Send 'echo' request to Titanic ZMsg request = new ZMsg(); request.add("echo"); request.add("Hello world"); ZMsg reply = serviceCall(session, "titanic.request", request); ZFrame uuid = null; if (reply != null) { uuid = reply.pop(); reply.destroy(); uuid.print("I: request UUID "); } // 2. Wait until we get a reply while (!Thread.currentThread().isInterrupted()) { Thread.sleep(100); request = new ZMsg(); request.add(uuid.duplicate()); reply = serviceCall(session, "titanic.reply", request); if (reply != null) { String replyString = reply.getLast().toString(); System.out.printf("Reply: %s\n", replyString); reply.destroy(); // 3. Close request request = new ZMsg(); request.add(uuid.duplicate()); reply = serviceCall(session, "titanic.close", request); reply.destroy(); break; } else { System.out.println("I: no reply yet, trying again..."); Thread.sleep(5000); // Try again in 5 seconds } } uuid.destroy(); session.destroy(); } } jeromq-0.6.0/src/test/java/guide/titanic.java000066400000000000000000000274031455771126300211150ustar00rootroot00000000000000package guide; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Paths; import java.util.UUID; import org.zeromq.ZContext; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; import org.zeromq.ZThread; import org.zeromq.ZThread.IAttachedRunnable; import org.zeromq.ZThread.IDetachedRunnable; public class titanic { // Return a new UUID as a printable character string // Caller must free returned string when finished with it static String generateUUID() { return UUID.randomUUID().toString(); } private static final String TITANIC_DIR = ".titanic"; // Returns freshly allocated request filename for given UUID private static String requestFilename(String uuid) { return String.format("%s/%s.req", TITANIC_DIR, uuid); } // Returns freshly allocated reply filename for given UUID private static String replyFilename(String uuid) { return String.format("%s/%s.rep", TITANIC_DIR, uuid); } // .split Titanic request service // The {{titanic.request}} task waits for requests to this service. It // writes each request to disk and returns a UUID to the client. The client // picks up the reply asynchronously using the {{titanic.reply}} service: static class TitanicRequest implements IAttachedRunnable { @Override public void run(Object[] args, ZContext ctx, Socket pipe) { mdwrkapi worker = new mdwrkapi( "tcp://localhost:5555", "titanic.request", false ); ZMsg reply = null; while (true) { // Send reply if it's not null // And then get next request from broker ZMsg request = worker.receive(reply); if (request == null) break; // Interrupted, exit // Ensure message directory exists new File(TITANIC_DIR).mkdirs(); // Generate UUID and save message to disk String uuid = generateUUID(); String filename = requestFilename(uuid); try (DataOutputStream file = new DataOutputStream(Files.newOutputStream(Paths.get(filename)))) { ZMsg.save(request, file); } catch (IOException e) { e.printStackTrace(); } request.destroy(); // Send UUID through to message queue reply = new ZMsg(); reply.add(uuid); reply.send(pipe); // Now send UUID back to client // Done by the mdwrk_recv() at the top of the loop reply = new ZMsg(); reply.add("200"); reply.add(uuid); } worker.destroy(); } } // .split Titanic reply service // The {{titanic.reply}} task checks if there's a reply for the specified // request (by UUID), and returns a 200 (OK), 300 (Pending), or 400 // (Unknown) accordingly: static class TitanicReply implements IDetachedRunnable { @Override public void run(Object[] args) { mdwrkapi worker = new mdwrkapi( "tcp://localhost:5555", "titanic.reply", false ); ZMsg reply = null; while (true) { ZMsg request = worker.receive(reply); if (request == null) break; // Interrupted, exit String uuid = request.popString(); String reqFilename = requestFilename(uuid); String repFilename = replyFilename(uuid); if (new File(repFilename).exists()) { try (DataInputStream file = new DataInputStream(Files.newInputStream(Paths.get(repFilename)))) { reply = ZMsg.load(file); reply.push("200"); } catch (IOException e) { e.printStackTrace(); } } else { reply = new ZMsg(); if (new File(reqFilename).exists()) reply.push("300"); //Pending else reply.push("400"); //Unknown } request.destroy(); } worker.destroy(); } } // .split Titanic close task // The {{titanic.close}} task removes any waiting replies for the request // (specified by UUID). It's idempotent, so it is safe to call more than // once in a row: static class TitanicClose implements IDetachedRunnable { @Override public void run(Object[] args) { mdwrkapi worker = new mdwrkapi( "tcp://localhost:5555", "titanic.close", false ); ZMsg reply = null; while (true) { ZMsg request = worker.receive(reply); if (request == null) break; // Interrupted, exit String uuid = request.popString(); String req_filename = requestFilename(uuid); String rep_filename = replyFilename(uuid); new File(rep_filename).delete(); new File(req_filename).delete(); request.destroy(); reply = new ZMsg(); reply.add("200"); } worker.destroy(); } } // .split worker task // This is the main thread for the Titanic worker. It starts three child // threads; for the request, reply, and close services. It then dispatches // requests to workers using a simple brute force disk queue. It receives // request UUIDs from the {{titanic.request}} service, saves these to a // disk file, and then throws each request at MDP workers until it gets a // response. public static void main(String[] args) { boolean verbose = (args.length > 0 && "-v".equals(args[0])); try (ZContext ctx = new ZContext()) { Socket requestPipe = ZThread.fork(ctx, new TitanicRequest()); ZThread.start(new TitanicReply()); ZThread.start(new TitanicClose()); Poller poller = ctx.createPoller(1); poller.register(requestPipe, ZMQ.Poller.POLLIN); // Main dispatcher loop while (true) { // We'll dispatch once per second, if there's no activity int rc = poller.poll(1000); if (rc == -1) break; // Interrupted if (poller.pollin(0)) { // Ensure message directory exists new File(TITANIC_DIR).mkdirs(); // Append UUID to queue, prefixed with '-' for pending ZMsg msg = ZMsg.recvMsg(requestPipe); if (msg == null) break; // Interrupted String uuid = msg.popString(); try (BufferedWriter wfile = new BufferedWriter(new FileWriter(TITANIC_DIR + "/queue", true))) { wfile.write("-" + uuid + "\n"); } catch (IOException e) { e.printStackTrace(); break; } msg.destroy(); } // Brute force dispatcher // "?........:....:....:....:............:"; byte[] entry = new byte[37]; try (RandomAccessFile file = new RandomAccessFile(TITANIC_DIR + "/queue", "rw")) { while (file.read(entry) > 0) { // UUID is prefixed with '-' if still waiting if (entry[0] == '-') { if (verbose) System.out.printf("I: processing request %s\n", new String(entry, 1, entry.length - 1, ZMQ.CHARSET)); if (serviceSuccess(new String(entry, 1, entry.length - 1, ZMQ.CHARSET))) { // Mark queue entry as processed file.seek(file.getFilePointer() - 37); file.writeBytes("+"); file.seek(file.getFilePointer() + 36); } } // Skip end of line, LF or CRLF if (file.readByte() == '\r') file.readByte(); if (Thread.currentThread().isInterrupted()) break; } } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } } } } // .split try to call a service // Here, we first check if the requested MDP service is defined or not, // using a MMI lookup to the Majordomo broker. If the service exists, we // send a request and wait for a reply using the conventional MDP client // API. This is not meant to be fast, just very simple: static boolean serviceSuccess(String uuid) { // Load request message, service will be first frame String filename = requestFilename(uuid); // If the client already closed request, treat as successful if (!new File(filename).exists()) return true; DataInputStream file = null; ZMsg request; try { file = new DataInputStream(Files.newInputStream(Paths.get(filename))); request = ZMsg.load(file); } catch (IOException e) { e.printStackTrace(); return true; } finally { try { if (file != null) file.close(); } catch (IOException e) { } } ZFrame service = request.pop(); String serviceName = service.toString(); // Create MDP client session with short timeout mdcliapi client = new mdcliapi("tcp://localhost:5555", false); client.setTimeout(1000); // 1 sec client.setRetries(1); // only 1 retry // Use MMI protocol to check if service is available ZMsg mmiRequest = new ZMsg(); mmiRequest.add(service); ZMsg mmiReply = client.send("mmi.service", mmiRequest); boolean serviceOK = (mmiReply != null && mmiReply.getFirst().toString().equals("200")); mmiReply.destroy(); boolean result = false; if (serviceOK) { ZMsg reply = client.send(serviceName, request); if (reply != null) { filename = replyFilename(uuid); DataOutputStream ofile; try { ofile = new DataOutputStream(Files.newOutputStream(Paths.get(filename))); ZMsg.save(reply, ofile); } catch (IOException e) { e.printStackTrace(); return true; } finally { try { if (file != null) file.close(); } catch (IOException e) { } } result = true; } reply.destroy(); } else request.destroy(); client.destroy(); return result; } } jeromq-0.6.0/src/test/java/guide/tripping.java000066400000000000000000000123621455771126300213140ustar00rootroot00000000000000package guide; import org.zeromq.*; import org.zeromq.ZMQ.Socket; /** * Round-trip demonstrator. Broker, Worker and Client are mocked as separate * threads. */ public class tripping { static class Broker implements Runnable { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket frontend = ctx.createSocket(SocketType.ROUTER); Socket backend = ctx.createSocket(SocketType.ROUTER); frontend.setHWM(0); backend.setHWM(0); frontend.bind("tcp://*:5555"); backend.bind("tcp://*:5556"); while (!Thread.currentThread().isInterrupted()) { ZMQ.Poller items = ctx.createPoller(2); items.register(frontend, ZMQ.Poller.POLLIN); items.register(backend, ZMQ.Poller.POLLIN); if (items.poll() == -1) break; // Interrupted if (items.pollin(0)) { ZMsg msg = ZMsg.recvMsg(frontend); if (msg == null) break; // Interrupted ZFrame address = msg.pop(); address.destroy(); msg.addFirst(new ZFrame("W")); msg.send(backend); } if (items.pollin(1)) { ZMsg msg = ZMsg.recvMsg(backend); if (msg == null) break; // Interrupted ZFrame address = msg.pop(); address.destroy(); msg.addFirst(new ZFrame("C")); msg.send(frontend); } items.close(); } } } } static class Worker implements Runnable { @Override public void run() { try (ZContext ctx = new ZContext()) { Socket worker = ctx.createSocket(SocketType.DEALER); worker.setHWM(0); worker.setIdentity("W".getBytes(ZMQ.CHARSET)); worker.connect("tcp://localhost:5556"); while (!Thread.currentThread().isInterrupted()) { ZMsg msg = ZMsg.recvMsg(worker); msg.send(worker); } } } } static class Client implements Runnable { private static int SAMPLE_SIZE = 10000; @Override public void run() { try (ZContext ctx = new ZContext()) { Socket client = ctx.createSocket(SocketType.DEALER); client.setHWM(0); client.setIdentity("C".getBytes(ZMQ.CHARSET)); client.connect("tcp://localhost:5555"); System.out.println("Setting up test"); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } int requests; long start; System.out.println("Synchronous round-trip test"); start = System.currentTimeMillis(); for (requests = 0; requests < SAMPLE_SIZE; requests++) { ZMsg req = new ZMsg(); req.addString("hello"); req.send(client); ZMsg.recvMsg(client).destroy(); } long now = System.currentTimeMillis(); System.out.printf( " %d calls/second\n", (1000 * SAMPLE_SIZE) / (now - start) ); System.out.println("Asynchronous round-trip test"); start = System.currentTimeMillis(); for (requests = 0; requests < SAMPLE_SIZE; requests++) { ZMsg req = new ZMsg(); req.addString("hello"); req.send(client); } for (requests = 0; requests < SAMPLE_SIZE && !Thread.currentThread() .isInterrupted(); requests++) { ZMsg.recvMsg(client).destroy(); } long now2 = System.currentTimeMillis(); System.out.printf( " %d calls/second\n", (1000 * SAMPLE_SIZE) / (now2 - start) ); } } } public static void main(String[] args) { if (args.length == 1) Client.SAMPLE_SIZE = Integer.parseInt(args[0]); Thread brokerThread = new Thread(new Broker()); Thread workerThread = new Thread(new Worker()); Thread clientThread = new Thread(new Client()); brokerThread.setDaemon(true); workerThread.setDaemon(true); brokerThread.start(); workerThread.start(); clientThread.start(); try { clientThread.join(); workerThread.interrupt(); brokerThread.interrupt(); Thread.sleep(200);// give them some time } catch (InterruptedException e) { } } } jeromq-0.6.0/src/test/java/guide/version.java000066400000000000000000000005241455771126300211420ustar00rootroot00000000000000package guide; import org.zeromq.ZMQ; // Report 0MQ version public class version { public static void main(String[] args) { String version = ZMQ.getVersionString(); int fullVersion = ZMQ.getFullVersion(); System.out.printf("Version string: %s, Version int: %d%n", version, fullVersion ); } } jeromq-0.6.0/src/test/java/guide/wuclient.java000066400000000000000000000032001455771126300213010ustar00rootroot00000000000000package guide; import java.util.StringTokenizer; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Weather update client in Java // Connects SUB socket to tcp://localhost:5556 // Collects weather updates and finds avg temp in zipcode // public class wuclient { public static void main(String[] args) { try (ZContext context = new ZContext()) { // Socket to talk to server System.out.println("Collecting updates from weather server"); ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); subscriber.connect("tcp://localhost:5556"); // Subscribe to zipcode, default is NYC, 10001 String filter = (args.length > 0) ? args[0] : "10001 "; subscriber.subscribe(filter.getBytes(ZMQ.CHARSET)); // Process 100 updates int update_nbr; long total_temp = 0; for (update_nbr = 0; update_nbr < 100; update_nbr++) { // Use trim to remove the tailing '0' character String string = subscriber.recvStr(0).trim(); StringTokenizer sscanf = new StringTokenizer(string, " "); int zipcode = Integer.parseInt(sscanf.nextToken()); int temperature = Integer.parseInt(sscanf.nextToken()); int relhumidity = Integer.parseInt(sscanf.nextToken()); total_temp += temperature; } System.out.printf("Average temperature for zipcode '%s' was %d.%n", filter, (int)(total_temp / update_nbr) ); } } } jeromq-0.6.0/src/test/java/guide/wuproxy.java000066400000000000000000000016101455771126300212070ustar00rootroot00000000000000package guide; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZContext; /** * Weather proxy device. */ public class wuproxy { public static void main(String[] args) { // Prepare our context and sockets try (ZContext context = new ZContext()) { // This is where the weather server sits Socket frontend = context.createSocket(SocketType.SUB); frontend.connect("tcp://192.168.55.210:5556"); // This is our public endpoint for subscribers Socket backend = context.createSocket(SocketType.PUB); backend.bind("tcp://10.1.1.0:8100"); // Subscribe on everything frontend.subscribe(ZMQ.SUBSCRIPTION_ALL); // Run the proxy until the user interrupts us ZMQ.proxy(frontend, backend, null); } } } jeromq-0.6.0/src/test/java/guide/wuserver.java000066400000000000000000000024271455771126300213430ustar00rootroot00000000000000package guide; import java.util.Random; import org.zeromq.SocketType; import org.zeromq.ZMQ; import org.zeromq.ZContext; // // Weather update server in Java // Binds PUB socket to tcp://*:5556 // Publishes random weather updates // public class wuserver { public static void main(String[] args) { // Prepare our context and publisher try (ZContext context = new ZContext()) { ZMQ.Socket publisher = context.createSocket(SocketType.PUB); publisher.bind("tcp://*:5556"); publisher.bind("ipc://weather"); // Initialize random number generator Random srandom = new Random(System.currentTimeMillis()); while (!Thread.currentThread().isInterrupted()) { // Get values that will fool the boss int zipcode, temperature, relhumidity; zipcode = 10000 + srandom.nextInt(10000); temperature = srandom.nextInt(215) - 80 + 1; relhumidity = srandom.nextInt(50) + 10 + 1; // Send message to all subscribers String update = String.format( "%05d %d %d", zipcode, temperature, relhumidity ); publisher.send(update, 0); } } } } jeromq-0.6.0/src/test/java/org/000077500000000000000000000000001455771126300163035ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/000077500000000000000000000000001455771126300176205ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/ByteBuffersTest.java000066400000000000000000000160311455771126300235440ustar00rootroot00000000000000package org.zeromq; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class ByteBuffersTest { static class Client extends Thread { private final int port; public Client(int port) { this.port = port; } @Override public void run() { System.out.println("Start client thread "); ZMQ.Context context = ZMQ.context(1); Socket pullConnect = context.socket(SocketType.PULL); pullConnect.connect("tcp://127.0.0.1:" + port); pullConnect.recv(0); pullConnect.close(); context.close(); System.out.println("Stop client thread "); } } @Test public void testByteBufferSend() throws InterruptedException, IOException { int port = Utils.findOpenPort(); ZMQ.Context context = ZMQ.context(1); ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.nativeOrder()); try (Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL)) { pull.bind("tcp://*:" + port); push.connect("tcp://localhost:" + port); bb.put("PING".getBytes(ZMQ.CHARSET)); bb.flip(); Thread.sleep(1000); push.sendByteBuffer(bb, 0); String actual = new String(pull.recv(), ZMQ.CHARSET); assertEquals("PING", actual); } finally { try { context.term(); } catch (Exception ignore) { } } } @Test public void testByteBufferRecv() throws InterruptedException, IOException { int port = Utils.findOpenPort(); ZMQ.Context context = ZMQ.context(1); ByteBuffer bb = ByteBuffer.allocate(6).order(ByteOrder.nativeOrder()); ZMQ.Socket push = null; ZMQ.Socket pull = null; try { push = context.socket(SocketType.PUSH); pull = context.socket(SocketType.PULL); pull.bind("tcp://*:" + port); push.connect("tcp://localhost:" + port); Thread.sleep(1000); push.send("PING".getBytes(ZMQ.CHARSET), 0); pull.recvByteBuffer(bb, 0); bb.flip(); byte[] b = new byte[bb.remaining()]; bb.duplicate().get(b); assertEquals("PING", new String(b, ZMQ.CHARSET)); } finally { try { push.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { pull.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { context.term(); } catch (Exception ignore) { ignore.printStackTrace(); } } } @Test public void testByteBufferLarge() throws IOException { int port = Utils.findOpenPort(); ZMQ.Context context = ZMQ.context(1); int[] array = new int[2048 * 2000]; for (int i = 0; i < array.length; ++i) { array[i] = i; } ByteBuffer bSend = ByteBuffer.allocate(Integer.SIZE / 8 * array.length).order(ByteOrder.nativeOrder()); bSend.asIntBuffer().put(array); ByteBuffer bRec = ByteBuffer.allocate(bSend.capacity()).order(ByteOrder.nativeOrder()); int size = bSend.capacity() / (1024 * 1024); System.out.println("Test sending large message (~" + size + "Mb)"); ZMQ.Socket push = null; ZMQ.Socket pull = null; try { push = context.socket(SocketType.PUSH); pull = context.socket(SocketType.PULL); pull.bind("tcp://*:" + port); push.connect("tcp://localhost:" + port); Thread.sleep(1000); long start = System.currentTimeMillis(); push.sendByteBuffer(bSend, 0); pull.recvByteBuffer(bRec, 0); long end = System.currentTimeMillis(); System.out.println("Received ~" + size + "Mb msg in " + (end - start) + " millisec."); bRec.flip(); assertEquals(bSend, bRec); } catch (Exception e) { e.printStackTrace(); Assert.fail(); } finally { try { push.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { pull.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { context.term(); } catch (Exception ignore) { ignore.printStackTrace(); } } } @Test public void testByteBufferLargeDirect() throws IOException { int port = Utils.findOpenPort(); ZMQ.Context context = ZMQ.context(1); int[] array = new int[2048 * 2000]; for (int i = 0; i < array.length; ++i) { array[i] = i; } ByteBuffer bSend = ByteBuffer.allocateDirect(Integer.SIZE / 8 * array.length).order(ByteOrder.nativeOrder()); bSend.asIntBuffer().put(array); ByteBuffer bRec = ByteBuffer.allocateDirect(bSend.capacity()).order(ByteOrder.nativeOrder()); int[] recArray = new int[array.length]; int size = bSend.capacity() / (1024 * 1024); System.out.println("Test sending direct large message (~" + size + "Mb)"); ZMQ.Socket push = null; ZMQ.Socket pull = null; try { push = context.socket(SocketType.PUSH); pull = context.socket(SocketType.PULL); pull.bind("tcp://*:" + port); push.connect("tcp://localhost:" + port); Thread.sleep(1000); long start = System.currentTimeMillis(); push.sendByteBuffer(bSend, 0); pull.recvByteBuffer(bRec, 0); long end = System.currentTimeMillis(); System.out.println("Received ~" + size + "Mb msg in " + (end - start) + " millisec."); bRec.flip(); bRec.asIntBuffer().get(recArray); assertArrayEquals(array, recArray); } catch (Exception e) { e.printStackTrace(); Assert.fail(); } finally { try { push.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { pull.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { context.term(); } catch (Exception ignore) { ignore.printStackTrace(); } } } } jeromq-0.6.0/src/test/java/org/zeromq/DealerDealerTest.java000066400000000000000000000102471455771126300236400ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.Deque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQ.Context; public class DealerDealerTest { private static final class Client implements Runnable { private final Context context; private final boolean verbose; private final String host; private final int messagesCount; private final Deque queue; private int missed = 0; private int reconnects = 0; private int count = 0; private int received = 0; private Client(Context context, boolean verbose, String host, int messagesCount, Deque queue) { this.context = context; this.verbose = verbose; this.host = host; this.messagesCount = messagesCount; this.queue = queue; } @Override public void run() { ZMQ.Socket worker = context.socket(SocketType.DEALER); worker.connect(host); int msg = messagesCount; while (msg-- > 0) { String received = worker.recvStr(); String expected = queue.pop(); count++; while (!expected.equals(received)) { count++; missed++; System.out.println("Missed " + expected + " (received " + received + ")"); expected = queue.pop(); } this.received++; if (verbose) { System.out.println("Received " + received); } if (count % 10 == 0) { worker.disconnect(host); reconnects++; if (verbose) { System.out.println("Reconnecting..."); } worker.close(); worker = context.socket(SocketType.DEALER); worker.connect(host); } if (count % 100 == 0) { System.out.println( "Received: " + this.received + " missed: " + missed + " reconnects: " + reconnects); } } worker.close(); } } @Test @Ignore public void testIssue335() throws InterruptedException, IOException { final boolean verbose = false; final int messagesCount = 1000; final ZMQ.Context context = ZMQ.context(1); final Deque queue = new LinkedBlockingDeque<>(); final String host = "tcp://localhost:" + Utils.findOpenPort(); final Runnable server = () -> { final ZMQ.Socket server1 = context.socket(SocketType.DEALER); server1.bind(host); int msg = messagesCount; while (msg-- > 0) { final String payload = Integer.toString(msg); queue.add(payload); if (!server1.send(payload)) { System.out.println("Send failed"); } zmq.ZMQ.msleep(10); } server1.close(); }; final Client client = new Client(context, verbose, host, messagesCount, queue); ExecutorService executor = Executors.newFixedThreadPool(2); executor.submit(server); executor.submit(client); long start = System.currentTimeMillis(); executor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS); long end = System.currentTimeMillis(); System.out.println("Done in " + (end - start) + " millis."); assertThat(client.missed, is(0)); assertThat(client.reconnects, is(messagesCount / 10)); assertThat(client.count, is(client.received)); context.close(); } } jeromq-0.6.0/src/test/java/org/zeromq/HighWatermarkTest.java000066400000000000000000000254561455771126300240740ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.security.SecureRandom; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; import org.junit.Test; import zmq.util.AndroidProblematic; public class HighWatermarkTest { public static final int N_MESSAGES = 30000; public static final int MESSAGE_SIZE = 50; public static final int FILL_WATERMARK = 3000; public static final int TRACE = 7000; public static class Dispatcher implements Runnable { private final String control; private final String dispatch; private final boolean trace; private final String msg; public Dispatcher(String msg, String dispatch, String control, boolean trace) { this.msg = msg; this.dispatch = dispatch; this.control = control; this.trace = trace; } @Override public void run() { Thread.currentThread().setName("Dispatcher"); ZContext context = new ZContext(1); // Socket to send messages on ZMQ.Socket sender = context.createSocket(SocketType.PUSH); sender.setImmediate(false); sender.bind(dispatch); ZMQ.Socket controller = context.createSocket(SocketType.SUB); controller.subscribe(ZMQ.SUBSCRIPTION_ALL); controller.connect(control); try { System.out.println("Sending " + N_MESSAGES + " tasks (" + MESSAGE_SIZE + "b) to workers\n"); // The first message is "0" and signals start of batch sender.send("0", 0); System.out.println("Started dispatcher on " + dispatch); // Send N_MESSAGES tasks for (int taskNbr = 0; taskNbr < N_MESSAGES; taskNbr++) { sender.send(taskNbr + " - " + msg, 0); if (trace) { System.out.println(taskNbr + " - Dispatcher sent msg"); } } System.out.println("Dispatcher finished, awaiting for collector finish"); controller.recvStr(); // We can finish NOW! } finally { if (trace) { System.out.println("Dispatcher closing."); } context.close(); System.out.println("Dispatcher done."); } } } public static class Worker implements Runnable { private final String control; private final String dispatch; private final String collect; private final boolean trace; private final int index; public Worker(String dispatch, String collect, String control, int index, boolean trace) { this.dispatch = dispatch; this.collect = collect; this.control = control; this.index = index; this.trace = trace; } @Override public void run() { Thread.currentThread().setName("Worker #" + index); ZContext context = new ZContext(1); // Socket to receive messages on ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.setImmediate(false); receiver.connect(dispatch); // Socket to send messages to ZMQ.Socket sender = context.createSocket(SocketType.PUSH); sender.setImmediate(false); sender.connect(collect); ZMQ.Socket controller = context.createSocket(SocketType.SUB); controller.subscribe("FINISH"); controller.connect(control); ZMQ.Poller poller = context.createPoller(3); poller.register(receiver, ZMQ.Poller.POLLIN); poller.register(sender, ZMQ.Poller.POLLOUT); poller.register(controller, ZMQ.Poller.POLLIN); int idx = 0; try { System.out.println("Started worker process #" + index); // Process tasks forever while (!Thread.currentThread().isInterrupted()) { poller.poll(1000); boolean in = poller.pollin(0); boolean out = poller.pollout(1); boolean ctrl = poller.pollin(2); if (in && out) { String msg = new String(receiver.recv(0), ZMQ.CHARSET).trim(); // Simple progress indicator for the viewer if (trace) { System.out.println("Worker #" + index + " recv " + msg); } else { if (idx % TRACE == 0) { System.out.println("Worker #" + index + " recv " + idx + " messages"); } } ++idx; // the pipes reach the watermark once in a while if (idx % FILL_WATERMARK == 10) { LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS)); } // Send results to sink sender.send("#" + index + " - " + msg, 0); } if (ctrl) { break; } } } finally { if (trace) { System.out.println("Worker #" + index + " closing."); } poller.close(); context.close(); if (trace) { System.out.println("Worker #" + index + " done."); } } } } public static class Collector implements Runnable { private final String control; private final boolean trace; private final String collect; private final String msg; private final int workers; private final AtomicBoolean success = new AtomicBoolean(); public Collector(String msg, String collect, String control, int workers, boolean trace) { this.msg = msg; this.collect = collect; this.control = control; this.workers = workers; this.trace = trace; } @Override public void run() { Thread.currentThread().setName("Collector"); if (trace) { System.out.println("Started collector on " + collect); } // Prepare our context and socket ZContext context = new ZContext(1); ZMQ.Socket receiver = context.createSocket(SocketType.PULL); receiver.setImmediate(false); receiver.bind(collect); ZMQ.Socket controller = context.createSocket(SocketType.PUB); controller.bind(control); try { // Wait for start of batch String msg = new String(receiver.recv(0), ZMQ.CHARSET); if (trace) { System.out.println("Collector started"); } for (int taskNbr = 0; taskNbr < N_MESSAGES; taskNbr++) { if (taskNbr % FILL_WATERMARK == 10) { LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS)); } msg = new String(receiver.recv(0), ZMQ.CHARSET).trim(); if (trace) { System.out.println("Collector recv : " + taskNbr + " -> " + msg); } else if (taskNbr % TRACE == 0 || taskNbr == 100) { System.out.println("Collector recv : " + taskNbr + " messages "); } // Test received messages if (workers == 1) { if (msg.indexOf(" - " + taskNbr + " - ") != 2) { System.out.println(taskNbr + " - Message was not correct ! " + msg); break; } } if (!msg.endsWith(this.msg) && !msg.endsWith(" - 0")) { System.out.println(taskNbr + " - Message was not correct ! " + msg); break; } } controller.send("FINISH"); // Signal dispatcher to finish } finally { context.close(); System.out.println("Collector done."); } success.set(true); } } @Test public void testReliabilityOnWatermark() throws IOException, InterruptedException { testWatermark(1); } @Test @AndroidProblematic public void testReliabilityOnWatermark2() throws IOException, InterruptedException { testWatermark(2); } private void testWatermark(int workers) throws IOException, InterruptedException { long start = System.currentTimeMillis(); ExecutorService threadPool = Executors.newFixedThreadPool(workers + 2); String control = "tcp://localhost:" + Utils.findOpenPort(); String collect = "tcp://localhost:" + Utils.findOpenPort(); String dispatch = "tcp://localhost:" + Utils.findOpenPort(); String msg = randomString(MESSAGE_SIZE); Dispatcher dispatcher = new Dispatcher(msg, dispatch, control, false); Collector collector = new Collector(msg, collect, control, workers, false); threadPool.submit(dispatcher); threadPool.submit(collector); for (int idx = 0; idx < workers; ++idx) { threadPool.submit(new Worker(dispatch, collect, control, idx + 1, false)); } threadPool.shutdown(); threadPool.awaitTermination(120, TimeUnit.SECONDS); long end = System.currentTimeMillis(); assertThat(collector.success.get(), is(true)); System.out.println("Test done in " + (end - start) + " millis."); } /*--------------------------------------------------------------*/ private static final String ABC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final SecureRandom rnd = new SecureRandom(); // http://stackoverflow.com/a/157202 private static String randomString(int len) { StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { sb.append(ABC.charAt(rnd.nextInt(ABC.length()))); } return sb.toString(); } } jeromq-0.6.0/src/test/java/org/zeromq/ParanoidPiratServerWithLazyPiratClientTest.java000066400000000000000000000445431455771126300311140ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; public class ParanoidPiratServerWithLazyPiratClientTest { private static final int HEARTBEAT_LIVENESS = 3; // 3-5 is reasonable private static final int HEARTBEAT_INTERVAL = 1000; // msecs // Paranoid Pirate Protocol constants private static final String PPP_READY = "\001"; // Signals worker is ready private static final String PPP_HEARTBEAT = "\002"; // Signals worker heartbeat private static void failTest(String desc, ZMsg msg) { msg.dump(System.out); final StringBuilder builder = new StringBuilder(desc); msg.dump(builder); fail(builder.toString()); } private static final class Queue implements Runnable { private final int portQueue; private final int portWorkers; private final AtomicBoolean active = new AtomicBoolean(true); // Here we define the worker class; a structure and a set of functions that // as constructor, destructor, and methods on worker objects: private static class Worker { final ZFrame address; // Address of worker final String identity; // Printable identity final long expiry; // Expires at this time protected Worker(ZFrame address) { this.address = address; identity = new String(address.getData(), ZMQ.CHARSET); expiry = System.currentTimeMillis() + HEARTBEAT_INTERVAL * HEARTBEAT_LIVENESS; } // The ready method puts a worker to the end of the ready list: protected void ready(final List workers) { final Iterator it = workers.iterator(); while (it.hasNext()) { final Worker worker = it.next(); if (identity.equals(worker.identity)) { it.remove(); break; } } workers.add(this); } // The next method returns the next available worker address: protected static ZFrame next(final List workers) { final Worker worker = workers.remove(0); assertThat(worker, notNullValue()); return worker.address; } // The purge method looks for and kills expired workers. We hold workers // from oldest to most recent, so we stop at the first alive worker: protected static void purge(List workers) { final Iterator it = workers.iterator(); while (it.hasNext()) { final Worker worker = it.next(); if (System.currentTimeMillis() < worker.expiry) { break; } it.remove(); } } } public Queue(int portQueue, int portWorkers) { this.portQueue = portQueue; this.portWorkers = portWorkers; } // The main task is an LRU queue with heartbeating on workers so we can // detect crashed or blocked worker tasks: @Override public void run() { Thread.currentThread().setName("Queue"); final ZContext ctx = new ZContext(); final Socket frontend = ctx.createSocket(SocketType.ROUTER); final Socket backend = ctx.createSocket(SocketType.ROUTER); frontend.bind("tcp://*:" + portQueue); // For clients backend.bind("tcp://*:" + portWorkers); // For workers // List of available workers final List workers = new ArrayList<>(); // Send out heartbeats at regular intervals long heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; final Poller poller = ctx.createPoller(2); poller.register(backend, Poller.POLLIN); poller.register(frontend, Poller.POLLIN); while (active.get()) { final boolean workersAvailable = !workers.isEmpty(); final int rc = poller.poll(HEARTBEAT_INTERVAL); if (rc == -1) { break; // Interrupted } // Handle worker activity on backend if (poller.pollin(0)) { // Use worker address for LRU routing final ZMsg msg = ZMsg.recvMsg(backend); if (msg == null) { break; // Interrupted } // Any sign of life from worker means it's ready final ZFrame address = msg.unwrap(); final Worker worker = new Worker(address); worker.ready(workers); // Validate control message, or return reply to client if (msg.size() == 1) { final ZFrame frame = msg.getFirst(); final String data = new String(frame.getData(), ZMQ.CHARSET); if (!data.equals(PPP_READY) && !data.equals(PPP_HEARTBEAT)) { failTest("E: Queue ---- invalid message from worker", msg); } msg.destroy(); } else { msg.send(frontend); } } if (workersAvailable && poller.pollin(1)) { // Now get next client request, route to next worker final ZMsg msg = ZMsg.recvMsg(frontend); if (msg == null) { break; // Interrupted } msg.push(Worker.next(workers)); msg.send(backend); } // We handle heartbeating after any socket activity. First we send // heartbeats to any idle workers if it's time. Then we purge any // dead workers: if (System.currentTimeMillis() >= heartbeatAt) { for (final Worker worker : workers) { worker.address.send(backend, ZFrame.REUSE + ZFrame.MORE); final ZFrame frame = new ZFrame(PPP_HEARTBEAT); frame.send(backend, 0); } heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; } Worker.purge(workers); } // When we're done, clean up properly workers.clear(); ctx.close(); } } private static final class Worker implements Runnable { private final int portWorkers; private static final int INTERVAL_INIT = 1000; // Initial reconnect private static final int INTERVAL_MAX = 32000; // After exponential backoff // Helper function that returns a new configured socket // connected to the Paranoid Pirate queue private Worker(int portWorkers) { this.portWorkers = portWorkers; } private Socket workerSocket(ZContext ctx) { final Socket worker = ctx.createSocket(SocketType.DEALER); worker.connect("tcp://localhost:" + portWorkers); // Tell queue we're ready for work System.out.println("I: Worker - ready"); final ZFrame frame = new ZFrame(PPP_READY); frame.send(worker, 0); return worker; } // We have a single task, which implements the worker side of the // Paranoid Pirate Protocol (PPP). The interesting parts here are // the heartbeating, which lets the worker detect if the queue has // died, and vice-versa: @Override public void run() { Thread.currentThread().setName("Worker"); final ZContext ctx = new ZContext(); Socket worker = workerSocket(ctx); final Poller poller = ctx.createPoller(1); poller.register(worker, Poller.POLLIN); // If liveness hits zero, queue is considered disconnected int liveness = HEARTBEAT_LIVENESS; int interval = INTERVAL_INIT; // Send out heartbeats at regular intervals long heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; int cycles = 0; while (true) { final int rc = poller.poll(HEARTBEAT_INTERVAL); if (rc == -1) { break; // Interrupted } if (poller.pollin(0)) { // Get message // - 3-part envelope + content -> request // - 1-part HEARTBEAT -> heartbeat final ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) { break; // Interrupted } // To test the robustness of the queue implementation we // simulate various typical problems, such as the worker // crashing, or running very slowly. We do this after a few // cycles so that the architecture can get up and running // first: if (msg.size() == 3) { cycles++; if (cycles % 10 == 0) { System.out.println("I: Worker - simulating a crash"); msg.destroy(); break; } else if (cycles % 5 == 0) { System.out.println("I: Worker - simulating CPU overload"); try { Thread.sleep(3000); } catch (InterruptedException e) { break; } } System.out.println("I: Worker - normal reply"); msg.send(worker); liveness = HEARTBEAT_LIVENESS; try { Thread.sleep(1000); } catch (InterruptedException e) { break; } // Do some heavy work } else // When we get a heartbeat message from the queue, it means the // queue was (recently) alive, so reset our liveness indicator: if (msg.size() == 1) { final ZFrame frame = msg.getFirst(); if (PPP_HEARTBEAT.equals(new String(frame.getData(), ZMQ.CHARSET))) { liveness = HEARTBEAT_LIVENESS; } else { failTest("E: Worker - invalid message", msg); } msg.destroy(); } else { failTest("E: Worker - invalid message", msg); } interval = INTERVAL_INIT; } else // If the queue hasn't sent us heartbeats in a while, destroy the // socket and reconnect. This is the simplest most brutal way of // discarding any messages we might have sent in the meantime:// if (--liveness == 0) { System.out.println("W: Worker ---- heartbeat failure, can't reach queue"); System.out.printf("W: Worker ---- reconnecting in %sd msec\n", interval); try { Thread.sleep(interval); } catch (InterruptedException e) { e.printStackTrace(); } if (interval < INTERVAL_MAX) { interval *= 2; } worker.close(); worker = workerSocket(ctx); liveness = HEARTBEAT_LIVENESS; } // Send heartbeat to queue if it's time if (System.currentTimeMillis() > heartbeatAt) { heartbeatAt = System.currentTimeMillis() + HEARTBEAT_INTERVAL; System.out.println("I: Worker - heartbeat"); final ZFrame frame = new ZFrame(PPP_HEARTBEAT); frame.send(worker, 0); } } ctx.close(); } } private static final class Client implements Runnable { private final int portQueue; private static final int REQUEST_TIMEOUT = 2500; // msecs, (> 1000!) private static final int REQUEST_RETRIES = 3; // Before we abandon public Client(int portQueue) { this.portQueue = portQueue; } @Override public void run() { Thread.currentThread().setName("Client"); final ZContext ctx = new ZContext(); System.out.println("I: Client - connecting to server"); Socket client = ctx.createSocket(SocketType.REQ); assert (client != null); client.connect("tcp://localhost:" + portQueue); final Poller poller = ctx.createPoller(1); poller.register(client, Poller.POLLIN); int sequence = 0; int retriesLeft = REQUEST_RETRIES; while (retriesLeft > 0 && !Thread.currentThread().isInterrupted()) { // We send a request, then we work to get a reply final String request = String.format("%d", ++sequence); client.send(request); int expectReply = 1; while (expectReply > 0) { // Poll socket for a reply, with timeout int rc = poller.poll(REQUEST_TIMEOUT); if (rc == -1) { break; // Interrupted } // Here we process a server reply and exit our loop if the // reply is valid. If we didn't a reply we close the client // socket and resend the request. We try a number of times // before finally abandoning: if (poller.pollin(0)) { // We got a reply from the server, must match getSequence final String reply = client.recvStr(); if (reply == null) { break; // Interrupted } if (Integer.parseInt(reply) == sequence) { System.out.printf("I: Client - server replied OK (%s)\n", reply); retriesLeft = REQUEST_RETRIES; expectReply = 0; } else { System.out.printf("E: Client ---- malformed reply from server: %s\n", reply); } } else if (--retriesLeft == 0) { System.out.println("E: Client - server seems to be offline, abandoning"); break; } else { System.out.println("W: Client - no response from server, retrying"); // Old socket is confused; close it and open a new one poller.unregister(client); client.close(); System.out.println("I: Client - reconnecting to server"); client = ctx.createSocket(SocketType.REQ); client.connect("tcp://localhost:" + portQueue); poller.register(client, Poller.POLLIN); // Send request again, on new socket client.send(request); } } } ctx.close(); } } // @Test public void testRepeated() throws IOException, InterruptedException, ExecutionException { for (int idx = 0; idx < 300; ++idx) { System.out.println("+++++++++ " + idx); testIssue408(); } } @Test public void testIssue408() throws IOException, InterruptedException, ExecutionException { final int portQueue = Utils.findOpenPort(); final int portWorkers = Utils.findOpenPort(); final ExecutorService service = Executors.newFixedThreadPool(4); final long start = System.currentTimeMillis(); final Queue queue = new Queue(portQueue, portWorkers); service.submit(queue); final Future worker = service.submit(new Worker(portWorkers)); service.submit(() -> { try { worker.get(); System.out.println("I: Rebooter - restarting new worker after crash ++++++++++++"); service.submit(new Worker(portWorkers)); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }); final Future client = service.submit(new Client(portQueue)); client.get(); // client is terminated, time to stop the queue to complete the test. queue.active.set(false); service.shutdown(); assertThat(service.awaitTermination(20, TimeUnit.SECONDS), is(true)); final long end = System.currentTimeMillis(); System.out.printf("Test with Paranoid Server and Lazy client completed in %s millis%n", end - start); } } jeromq-0.6.0/src/test/java/org/zeromq/PubSubTest.java000066400000000000000000000136471455771126300225360ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class PubSubTest { @Test @Ignore public void testRaceConditionIssue322() throws IOException, InterruptedException { final ZMQ.Context context = ZMQ.context(1); final String address = "tcp://localhost:" + Utils.findOpenPort(); final byte[] msg = "abc".getBytes(); final int messagesNumber = 1000; //run publisher Runnable pub = () -> { Socket publisher = context.socket(SocketType.PUB); publisher.bind(address); int count = messagesNumber; while (count-- > 0) { publisher.send(msg); System.out.println("Send message " + count); } publisher.close(); }; //run subscriber Runnable sub = () -> { Socket subscriber = context.socket(SocketType.SUB); subscriber.connect(address); subscriber.subscribe(ZMQ.SUBSCRIPTION_ALL); int count = messagesNumber; while (count-- > 0) { subscriber.recv(); System.out.println("Received message " + count); } subscriber.close(); }; ExecutorService executor = Executors.newFixedThreadPool(2, r -> { Thread thread = new Thread(r); thread.setUncaughtExceptionHandler((t, e) -> e.printStackTrace()); return thread; }); executor.submit(sub); zmq.ZMQ.sleep(1); executor.submit(pub); executor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS); context.close(); } @Test @Ignore public void testPubConnectSubBindIssue289and342() throws IOException { ZMQ.Context context = ZMQ.context(1); Socket pub = context.socket(SocketType.XPUB); assertThat(pub, notNullValue()); Socket sub = context.socket(SocketType.SUB); assertThat(sub, notNullValue()); boolean rc = sub.subscribe(new byte[0]); assertThat(rc, is(true)); String host = "tcp://localhost:" + Utils.findOpenPort(); rc = sub.bind(host); assertThat(rc, is(true)); rc = pub.connect(host); assertThat(rc, is(true)); zmq.ZMQ.msleep(300); rc = pub.send("test"); assertThat(rc, is(true)); assertThat(sub.recvStr(), is("test")); pub.close(); sub.close(); context.term(); } @Test public void testUnsubscribeIssue554() throws Exception { final int port = Utils.findOpenPort(); final ExecutorService service = Executors.newFixedThreadPool(2); final Callable pub = () -> { final ZMQ.Context ctx = ZMQ.context(1); assertThat(ctx, notNullValue()); final Socket pubsocket = ctx.socket(SocketType.PUB); assertThat(pubsocket, notNullValue()); boolean rc = pubsocket.bind("tcp://*:" + port); assertThat(rc, is(true)); for (int idx = 1; idx <= 15; ++idx) { rc = pubsocket.sendMore("test/"); assertThat(rc, is(true)); rc = pubsocket.send("data" + idx); assertThat(rc, is(true)); System.out.printf("Send-%d/", idx); ZMQ.msleep(100); } pubsocket.close(); ctx.close(); return true; }; final Callable sub = new Callable() { @Override public Integer call() { final ZMQ.Context ctx = ZMQ.context(1); assertThat(ctx, notNullValue()); final ZMQ.Socket sub = ctx.socket(SocketType.SUB); assertThat(sub, notNullValue()); boolean rc = sub.setReceiveTimeOut(3000); assertThat(rc, is(true)); rc = sub.subscribe("test/"); assertThat(rc, is(true)); rc = sub.connect("tcp://localhost:" + port); assertThat(rc, is(true)); System.out.println("[SUB]"); int received = receive(sub, 5); assertThat(received > 1, is(true)); // unsubscribe from the topic and verify that we don't receive messages anymore rc = sub.unsubscribe("test/"); assertThat(rc, is(true)); System.out.printf("%n[UNSUB]%n"); received = receive(sub, 10); sub.close(); ctx.close(); return received; } private int receive(ZMQ.Socket socket, int maxSeconds) { int received = 0; long current = System.currentTimeMillis(); long end = current + maxSeconds * 1000; while (current < end) { ZMsg msg = ZMsg.recvMsg(socket); current = System.currentTimeMillis(); if (msg == null) { continue; } ++received; } return received; } }; final Future rc = service.submit(sub); final Future pubf = service.submit(pub); service.shutdown(); service.awaitTermination(60, TimeUnit.SECONDS); final int receivedAfterUnsubscription = rc.get(); assertThat(receivedAfterUnsubscription, is(0)); assertThat(pubf.get(), is(true)); } } jeromq-0.6.0/src/test/java/org/zeromq/PushPullTest.java000066400000000000000000000063401455771126300231020ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.junit.Test; import zmq.util.Utils; public class PushPullTest { private static final int REPETITIONS = 10; static class Sender implements Runnable { private final CountDownLatch latch; private final int port; public Sender(CountDownLatch latch, int port) { this.latch = latch; this.port = port; } @Override public void run() { String address = "tcp://*:" + port; ZMQ.Context context = ZMQ.context(1); assertThat(context, notNullValue()); ZMQ.Socket socket = context.socket(SocketType.PUSH); assertThat(socket, notNullValue()); // Socket options boolean rc = socket.setLinger(1000); assertThat(rc, is(true)); rc = socket.bind(address); assertThat(rc, is(true)); // Ensure that receiver is "connected" before sending try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } for (int idx = 0; idx < REPETITIONS; ++idx) { rc = socket.send("data" + idx); assertThat(rc, is(true)); } socket.close(); context.close(); } } static class Receiver implements Runnable { private final CountDownLatch latch; private final int port; public Receiver(CountDownLatch latch, int port) { this.latch = latch; this.port = port; } @Override public void run() { String address = "tcp://localhost:" + port; ZMQ.Context context = ZMQ.context(1); assertThat(context, notNullValue()); ZMQ.Socket socket = context.socket(SocketType.PULL); assertThat(socket, notNullValue()); // Options Section boolean rc = socket.setRcvHWM(1); assertThat(rc, is(true)); rc = socket.connect(address); assertThat(rc, is(true)); latch.countDown(); for (int idx = 0; idx < REPETITIONS; ++idx) { String recvd = socket.recvStr(); assertThat(recvd, is("data" + idx)); ZMQ.msleep(10); } socket.close(); context.close(); } } @Test public void testIssue131() throws InterruptedException, IOException { CountDownLatch latch = new CountDownLatch(1); final int port = Utils.findOpenPort(); Thread sender = new Thread(new Sender(latch, port)); Thread receiver = new Thread(new Receiver(latch, port)); // Start sender before receiver and use latch to ensure that receiver has connected // before sender is sending messages sender.start(); receiver.start(); sender.join(); receiver.join(); } } jeromq-0.6.0/src/test/java/org/zeromq/ReqRepTest.java000066400000000000000000000214401455771126300225220ustar00rootroot00000000000000package org.zeromq; import org.junit.Test; import org.zeromq.ZMQ.Socket; import zmq.util.AndroidProblematic; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class ReqRepTest { private interface GetServer { Server303 get(String address, boolean verbose); } private static final class Server532 extends Server303 { private final byte[] repMsg = new byte[1024 * 1024]; private Server532(String address, boolean verbose) { super(address, verbose); Arrays.fill(repMsg, (byte) 'e'); } @Override protected boolean send(int currentServCount, Socket responder, String msg) { int size = Math.min(Integer.parseInt(msg.substring(6)), repMsg.length); return responder.send(repMsg, 0, size, 0); } } private static class Server303 implements Callable { private final String address; private final boolean verbose; private Server303(String address, boolean verbose) { this.address = address; this.verbose = verbose; } @Override public Integer call() { int messagesReplied = 0; try ( ZMQ.Context context = ZMQ.context(1); ZMQ.Socket responder = context.socket(SocketType.REP)) { assertThat(responder, notNullValue()); boolean rc = responder.bind(address); assertThat(rc, is(true)); int count; for (count = 0; /* Server stops when receiving poison pill */; count++) { try { String incomingMessage = responder.recvStr(); assertThat(incomingMessage, notNullValue()); if ("end".equals(incomingMessage)) { // one client sent us poison pill, swallow it break; } rc = send(messagesReplied, responder, incomingMessage); assertThat(rc, is(true)); messagesReplied++; if (verbose && messagesReplied % 1000 == 0) { System.out.println("Served " + messagesReplied); } } catch (ZMQException ex) { if (ex.getErrorCode() != ZMQ.Error.EINTR.getCode()) { throw ex; } } } return count; } } protected boolean send(int currentServCount, ZMQ.Socket responder, String incomingMessage) { return responder.send( String.format( "Server Replied [%1$s/%2$s] of %3$s", currentServCount, Thread.currentThread().getId(), incomingMessage)); } } private static final class Client303 implements Callable { private final AtomicBoolean keepRunning; private final String address; private final boolean verbose; private Client303(String address, AtomicBoolean keepRunning, boolean verbose) { this.keepRunning = keepRunning; this.address = address; this.verbose = verbose; } @Override public Integer call() { try ( ZMQ.Context context = ZMQ.context(10); ZMQ.Socket socket = context.socket(SocketType.REQ)) { boolean rc = socket.connect(address); assertThat(rc, is(true)); int idx; for (idx = 0; keepRunning.get(); idx++) { long tid = Thread.currentThread().getId(); String msg = "hello-" + idx; if (verbose) { System.out.println(tid + " sending " + msg); } rc = socket.send(msg); assertThat(rc, is(true)); if (verbose) { System.out.println(tid + " waiting response"); } String s = socket.recvStr(); assertThat(s, notNullValue()); if (verbose) { System.out.println(tid + " client received [" + s + "]"); } } // send the poison pill to the server rc = socket.send("end"); assertThat(rc, is(true)); return idx; } } } private void runTest(GetServer getter) throws IOException, InterruptedException, ExecutionException { final int threads = 1; final int port = Utils.findOpenPort(); final String address = "tcp://localhost:" + port; final AtomicBoolean keepRunning = new AtomicBoolean(true); final boolean verbose = false; ExecutorService executor = Executors.newFixedThreadPool(1 + threads); Set> clientsMessages = new HashSet<>(threads); for (int idx = 0; idx < threads; ++idx) { clientsMessages.add(executor.submit(new Client303(address, keepRunning, verbose))); } Future serverMessages = executor.submit(getter.get(address, verbose)); // let messages flow for some time... ZMQ.sleep(4); keepRunning.set(false); int totalClientsMessages = 0; for (Future c : clientsMessages) { totalClientsMessages += c.get(); } assertThat(totalClientsMessages, is(serverMessages.get())); List runnables = executor.shutdownNow(); assertThat(runnables.size(), is(0)); } @Test(timeout = 5000) public void testIssue532() throws IOException, InterruptedException, ExecutionException { runTest(Server532::new); } @Test(timeout = 5000) public void testWaitForeverOnSignalerIssue303() throws IOException, InterruptedException, ExecutionException { runTest(Server303::new); } @Test(timeout = 5000) @AndroidProblematic // triggers OutofMemoryError on Android public void testDisconnectOnLargeMessageIssue334() throws Exception { final int msgSizeMB = 100; final ZMQ.Context context = ZMQ.context(1); final int oneMb = 1024 * 1024; final byte[] payloadBytes = new byte[msgSizeMB * oneMb]; for (int idx = 0; idx < msgSizeMB; ++idx) { int offset = oneMb * idx; Arrays.fill(payloadBytes, offset, offset + oneMb, (byte) ('a' + idx)); } final ZMsg request = new ZMsg(); request.add(payloadBytes); final String host = "localhost"; final int port = Utils.findOpenPort(); final String addr = "tcp://" + host + ":" + port; final CountDownLatch latch = new CountDownLatch(1); final ExecutorService executorService = Executors.newSingleThreadExecutor(); // simulates a server reply final Future senderf = executorService.submit(() -> { final Socket rep = context.socket(SocketType.REP); rep.bind(addr); latch.countDown(); // just send the message back... final ZMsg msg = ZMsg.recvMsg(rep); msg.send(rep); // shut down the socket to cause a disconnect while REQ socket receives the msg rep.close(); // btw.: setting linger time did not change the result return true; }); executorService.shutdown(); // wait till server socket is bound latch.await(); try ( final ZMQ.Socket req = context.socket(SocketType.REQ)) { req.connect(addr); request.send(req); final ZMsg response = ZMsg.recvMsg(req); // the messages should be equal assertThat(Arrays.equals(response.getLast().getData(), payloadBytes), is(true)); assertThat(senderf.get(), is(true)); } finally { context.close(); } } } jeromq-0.6.0/src/test/java/org/zeromq/SelectorProviderTest.java000066400000000000000000000031641455771126300246220ustar00rootroot00000000000000package org.zeromq; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.nio.channels.spi.SelectorProvider; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.zeromq.ZMQ.Socket; import zmq.Options; import zmq.io.net.Address.IZAddress; import zmq.io.net.SelectorProviderChooser; import zmq.util.Utils; public class SelectorProviderTest { public static class DefaultSelectorProviderChooser implements SelectorProviderChooser { public final AtomicInteger choosen = new AtomicInteger(0); @Override public SelectorProvider choose(IZAddress addr, Options options) { choosen.addAndGet(1); return SelectorProvider.provider(); } } @Test public void test() throws IOException { int port = Utils.findOpenPort(); try ( ZContext ctx = new ZContext(); Socket pull = ctx.createSocket(SocketType.PULL); Socket push = ctx.createSocket(SocketType.PUSH)) { DefaultSelectorProviderChooser chooser = new DefaultSelectorProviderChooser(); pull.setSelectorChooser(chooser); push.setSelectorChooser(chooser); pull.bind("tcp://*:" + port); push.connect("tcp://127.0.0.1:" + port); String expected = "hello"; push.send(expected); String actual = new String(pull.recv()); assertEquals(expected, actual); // Ensure that the choose method was indeed called for each socket. assertEquals(2, chooser.choosen.get()); } } } jeromq-0.6.0/src/test/java/org/zeromq/TemporaryFolderFinder.java000066400000000000000000000005041455771126300247300ustar00rootroot00000000000000package org.zeromq; /** * This class is a hacky way of making tests pass on Android devices. * Please feel free to amend. */ public class TemporaryFolderFinder { private TemporaryFolderFinder() { // no instantiation } public static String resolve(String file) { return file; } } jeromq-0.6.0/src/test/java/org/zeromq/TestDisconnectInprocZeromq.java000066400000000000000000000053641455771126300257750ustar00rootroot00000000000000package org.zeromq; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.Test; public class TestDisconnectInprocZeromq { @Test public void testDisconnectInproc() { int publicationsReceived = 0; boolean isSubscribed = false; ZContext ctx = new ZContext(); ZMQ.Socket pubSocket = ctx.createSocket(SocketType.XPUB); ZMQ.Socket subSocket = ctx.createSocket(SocketType.SUB); subSocket.subscribe("foo".getBytes()); pubSocket.bind("inproc://someInProcDescriptor"); int iteration = 0; ZMQ.Poller poller = ctx.createPoller(2); poller.register(subSocket, ZMQ.Poller.POLLIN); // read publications poller.register(pubSocket, ZMQ.Poller.POLLIN); // read subscriptions while (true) { poller.poll(500); if (poller.pollin(1)) { while (true) { byte[] buffer = pubSocket.recv(0); int msgSize = buffer.length; if (buffer[0] == 0) { assertTrue(isSubscribed); System.out.printf("unsubscribing from '%s'\n", new String(buffer, 1, msgSize - 1)); isSubscribed = false; } else { assert (!isSubscribed); System.out.printf("subscribing on '%s'\n", new String(buffer, 1, msgSize - 1)); isSubscribed = true; } if (!pubSocket.hasReceiveMore()) { break; // Last message part } } } if (poller.pollin(0)) { while (true) { byte[] buffer = subSocket.recv(0); int msgSize = buffer.length; System.out.printf("received on subscriber '%s'\n", new String(buffer, 0, msgSize)); if (!subSocket.hasReceiveMore()) { publicationsReceived++; break; // Last message part } } } if (iteration == 1) { subSocket.connect("inproc://someInProcDescriptor"); } if (iteration == 4) { subSocket.disconnect("inproc://someInProcDescriptor"); } if (iteration == 10) { break; } pubSocket.send("foo".getBytes(ZMQ.CHARSET), ZMQ.SNDMORE); pubSocket.send("this is foo!".getBytes(ZMQ.CHARSET), 0); iteration++; } assertEquals(3, publicationsReceived); assertTrue(!isSubscribed); ctx.close(); } } jeromq-0.6.0/src/test/java/org/zeromq/TestEventResolution.java000066400000000000000000000322311455771126300244710ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Error; import org.zeromq.ZMonitor.ProtocolCode; import zmq.SocketBase; import zmq.ZError; import zmq.util.function.Consumer; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; public class TestEventResolution { private interface SendEvent { void send(SocketBase s, String addr); } private ZEvent run1(SendEvent sender, int eventFilter) throws IOException { try (ZContext zctx = new ZContext(1); ZMQ.Socket s = zctx.createSocket(SocketType.PUB); ZMQ.Socket m = zctx.createSocket(SocketType.PAIR)) { s.monitor("inproc://TestEventResolution", eventFilter); m.connect("inproc://TestEventResolution"); sender.send(s.base(), "tcp://127.0.0.1:" + Utils.findOpenPort()); return ZEvent.recv(m); } } private ZEvent run2(SendEvent sender, int eventFilter) throws ExecutionException, InterruptedException, IOException { CompletableFuture eventFuture = new CompletableFuture<>(); try (ZContext zctx = new ZContext(1); ZMQ.Socket s = zctx.createSocket(SocketType.PUB)) { s.setEventHook(eventFuture::complete, eventFilter); sender.send(s.base(), "tcp://127.0.0.1:" + Utils.findOpenPort()); return eventFuture.get(); } } private void doTest(SendEvent sender, int eventFilter, Consumer check) throws ExecutionException, InterruptedException, IOException { ZEvent event1 = run1(sender, eventFilter); assertThat(event1.getAddress(), startsWith("tcp://127.0.0.1:")); check.accept(event1); ZEvent event2 = run2(sender, eventFilter); assertThat(event1.getAddress(), startsWith("tcp://127.0.0.1:")); check.accept(event2); } @Test(timeout = 1000) public void testFailed() { ZContext zctx = new ZContext(1); ZMQ.Socket m = zctx.createSocket(SocketType.PAIR); m.connect("inproc://TestEventResolution"); zctx.close(); ZMQException ex = Assert.assertThrows(ZMQException.class, () -> ZEvent.recv(m)); assertThat(ex.getErrorCode(), is(ZError.ETERM)); } @Test(timeout = 1000) public void testEventHandshakeFailedProtocol() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventHandshakeFailedProtocol(a, zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND), zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL, ev -> { ProtocolCode protocol = ev.getValue(); assertThat(protocol, is(ProtocolCode.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND)); assertThat(ev.isError(), is(true)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testEventHandshakeFailedAuth() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventHandshakeFailedAuth(a, 200), zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_AUTH, ev -> { int statusCode = ev.getValue(); assertThat(statusCode, is(200)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(true)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testEventDisconnected() throws IOException, ExecutionException, InterruptedException { try (SelectableChannel sc = SocketChannel.open()) { doTest((s, a) -> s.eventDisconnected(a, sc), zmq.ZMQ.ZMQ_EVENT_DISCONNECTED, ev -> { Object value = ev.getValue(); assertThat(value, is(sc)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(true)); assertThat(ev.isDebug(), is(false)); }); } } @Test(timeout = 1000) public void testEventClosed() throws IOException, ExecutionException, InterruptedException { try (SelectableChannel sc = SocketChannel.open()) { doTest((s, a) -> s.eventClosed(a, sc), zmq.ZMQ.ZMQ_EVENT_CLOSED, ev -> { Object value = ev.getValue(); assertThat(value, is(sc)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } } @Test(timeout = 1000) public void testEventListening() throws IOException, ExecutionException, InterruptedException { try (SelectableChannel sc = SocketChannel.open()) { doTest((s, a) -> s.eventListening(a, sc), zmq.ZMQ.ZMQ_EVENT_LISTENING, ev -> { Object value = ev.getValue(); assertThat(value, is(sc)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } } @Test(timeout = 1000) public void testEventConnected() throws IOException, ExecutionException, InterruptedException { try (SelectableChannel sc = SocketChannel.open()) { doTest((s, a) -> s.eventConnected(a, sc), zmq.ZMQ.ZMQ_EVENT_CONNECTED, ev -> { Object value = ev.getValue(); assertThat(value, is(sc)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } } @Test(timeout = 1000) public void testEventAccepted() throws IOException, ExecutionException, InterruptedException { try (SelectableChannel sc = SocketChannel.open()) { doTest((s, a) -> s.eventAccepted(a, sc), zmq.ZMQ.ZMQ_EVENT_ACCEPTED, ev -> { Object value = ev.getValue(); assertThat(value, is(sc)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } } @Test(timeout = 1000) public void testEventConnectDelayed() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventConnectDelayed(a, -1), zmq.ZMQ.ZMQ_EVENT_CONNECT_DELAYED, ev -> { Object value = ev.getValue(); assertThat(value, is(Error.NOERROR)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } @Test(timeout = 1000) public void testEventConnectRetried() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventConnectRetried(a, 10), zmq.ZMQ.ZMQ_EVENT_CONNECT_RETRIED, ev -> { Object value = ev.getValue(); assertThat(value, is(Duration.ofMillis(10))); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } @Test(timeout = 1000) public void testEventCloseFailed() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventCloseFailed(a, Error.EINTR.getCode()), zmq.ZMQ.ZMQ_EVENT_CLOSE_FAILED, ev -> { Error err = ev.getValue(); assertThat(err, is(Error.EINTR)); assertThat(ev.isError(), is(true)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testEventAcceptFailed() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventAcceptFailed(a, Error.EINTR.getCode()), zmq.ZMQ.ZMQ_EVENT_ACCEPT_FAILED, ev -> { Error err = ev.getValue(); assertThat(err, is(Error.EINTR)); assertThat(ev.isError(), is(true)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testEventBindFailed() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventBindFailed(a, Error.EADDRNOTAVAIL.getCode()), zmq.ZMQ.ZMQ_EVENT_BIND_FAILED, ev -> { Error err = ev.getValue(); assertThat(err, is(Error.EADDRNOTAVAIL)); assertThat(ev.isError(), is(true)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testEventHandshaken() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventHandshaken(a, -1), zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL, ev -> { int zmtp = ev.getValue(); assertThat(zmtp, is(-1)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } @Test(timeout = 1000) public void testEventHandshakeSucceeded() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventHandshakeSucceeded(a, 0), zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED, ev -> { Error err = ev.getValue(); assertThat(err, is(Error.NOERROR)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); }); } @Test(timeout = 1000) public void testEventHandshakeFailedNoDetail() throws ExecutionException, InterruptedException, IOException { doTest((s, a) -> s.eventHandshakeFailedNoDetail(a, Error.EFAULT.getCode()), zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL, ev -> { Error err = ev.getValue(); assertThat(err, is(Error.EFAULT)); assertThat(ev.isError(), is(true)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(false)); }); } @Test(timeout = 1000) public void testMonitorStop() throws ExecutionException, InterruptedException { CompletableFuture eventFuture = new CompletableFuture<>(); try (ZContext zctx = new ZContext(1); ZMQ.Socket s = zctx.createSocket(SocketType.PUB)) { s.setEventHook(eventFuture::complete, zmq.ZMQ.ZMQ_EVENT_MONITOR_STOPPED); } ZEvent ev = eventFuture.get(); assertThat(ev.getEvent(), is(ZMonitor.Event.MONITOR_STOPPED)); assertThat(ev.isError(), is(false)); assertThat(ev.isWarn(), is(false)); assertThat(ev.isInformation(), is(false)); assertThat(ev.isDebug(), is(true)); } } jeromq-0.6.0/src/test/java/org/zeromq/TestEvents.java000066400000000000000000000202371455771126300225730ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import org.junit.Test; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class TestEvents { @Test public void testEventConnectedContext() { try (Context context = new Context(1); Socket helper = context.socket(SocketType.REQ); Socket socket = context.socket(SocketType.REP); Socket monitor = context.socket(SocketType.PAIR) ) { int port = helper.bindToRandomPort("tcp://127.0.0.1"); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_CONNECTED)); monitor.connect("inproc://monitor.socket"); socket.connect("tcp://127.0.0.1:" + port); ZMQ.Event event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_CONNECTED, event.getEvent()); } } @Test public void testEventConnected() { try (ZContext context = new ZContext(1); Socket helper = context.createSocket(SocketType.REQ); Socket socket = context.createSocket(SocketType.REP); Socket monitor = context.createSocket(SocketType.PAIR) ) { int port = helper.bindToRandomPort("tcp://127.0.0.1"); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_CONNECTED)); monitor.connect("inproc://monitor.socket"); socket.connect("tcp://127.0.0.1:" + port); ZMQ.Event event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_CONNECTED, event.getEvent()); } } @Test public void testEventConnectDelayed() throws IOException { Context context = ZMQ.context(1); Socket socket = context.socket(SocketType.REP); Socket monitor = context.socket(SocketType.PAIR); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_CONNECT_DELAYED)); monitor.connect("inproc://monitor.socket"); int randomPort = Utils.findOpenPort(); socket.connect("tcp://127.0.0.1:" + randomPort); ZMQ.Event event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_CONNECT_DELAYED, event.getEvent()); socket.close(); monitor.close(); context.term(); } @Test public void testEventConnectRetried() throws InterruptedException, IOException { Context context = ZMQ.context(1); ZMQ.Event event; Socket socket = context.socket(SocketType.REP); Socket monitor = context.socket(SocketType.PAIR); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_CONNECT_RETRIED)); monitor.connect("inproc://monitor.socket"); int randomPort = Utils.findOpenPort(); socket.connect("tcp://127.0.0.1:" + randomPort); Thread.sleep(1000L); // on windows, this is required, otherwise test fails event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_CONNECT_RETRIED, event.getEvent()); socket.close(); monitor.close(); context.term(); } @Test public void testEventListening() { try (ZContext context = new ZContext(1); Socket socket = context.createSocket(SocketType.REP); Socket monitor = context.createSocket(SocketType.PAIR) ) { monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_LISTENING)); monitor.connect("inproc://monitor.socket"); socket.bindToRandomPort("tcp://127.0.0.1"); ZMQ.Event event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_LISTENING, event.getEvent()); } } @Test public void testEventBindFailed() { try (ZContext context = new ZContext(1); Socket helper = context.createSocket(SocketType.REP); Socket socket = context.createSocket(SocketType.REP); Socket monitor = context.createSocket(SocketType.PAIR) ) { ZMQ.Event event; int port = helper.bindToRandomPort("tcp://127.0.0.1"); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_BIND_FAILED)); monitor.connect("inproc://monitor.socket"); socket.bind("tcp://127.0.0.1:" + port); event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_BIND_FAILED, event.getEvent()); } } @Test public void testEventAccepted() { try (ZContext context = new ZContext(1); Socket helper = context.createSocket(SocketType.REP); Socket socket = context.createSocket(SocketType.REP); Socket monitor = context.createSocket(SocketType.PAIR) ) { ZMQ.Event event; monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_ACCEPTED)); monitor.connect("inproc://monitor.socket"); int port = socket.bindToRandomPort("tcp://127.0.0.1"); helper.connect("tcp://127.0.0.1:" + port); event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_ACCEPTED, event.getEvent()); } } @Test public void testEventClosed() { try (ZContext context = new ZContext(1); Socket monitor = context.createSocket(SocketType.PAIR) ) { ZMQ.Event event; Socket socket = context.createSocket(SocketType.REP); monitor.setReceiveTimeOut(100); socket.bindToRandomPort("tcp://127.0.0.1"); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_CLOSED)); monitor.connect("inproc://monitor.socket"); socket.close(); event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_CLOSED, event.getEvent()); } } @Test public void testEventDisconnected() { try (ZContext context = new ZContext(1); Socket socket = context.createSocket(SocketType.REP); Socket monitor = context.createSocket(SocketType.PAIR); Socket helper = context.createSocket(SocketType.REQ) ) { ZMQ.Event event; monitor.setReceiveTimeOut(100); int port = socket.bindToRandomPort("tcp://127.0.0.1"); helper.connect("tcp://127.0.0.1:" + port); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_DISCONNECTED)); monitor.connect("inproc://monitor.socket"); zmq.ZMQ.sleep(1); helper.close(); event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_DISCONNECTED, event.getEvent()); } } @Test public void testEventMonitorStopped() { Context context = ZMQ.context(1); ZMQ.Event event; Socket socket = context.socket(SocketType.REP); Socket monitor = context.socket(SocketType.PAIR); monitor.setReceiveTimeOut(100); assertTrue(socket.monitor("inproc://monitor.socket", ZMQ.EVENT_MONITOR_STOPPED)); monitor.connect("inproc://monitor.socket"); socket.monitor(null, 0); event = ZMQ.Event.recv(monitor); assertNotNull("No event was received", event); assertEquals(ZMQ.EVENT_MONITOR_STOPPED, event.getEvent()); socket.close(); monitor.close(); context.term(); } } jeromq-0.6.0/src/test/java/org/zeromq/TestPoller.java000066400000000000000000000074321455771126300225660ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class TestPoller { static class Client implements Runnable { private final AtomicBoolean received = new AtomicBoolean(); private final String address; private final CountDownLatch ready = new CountDownLatch(0); public Client(String addr) { this.address = addr; } @Override public void run() { ZMQ.Context context = ZMQ.context(1); Socket pullConnect = context.socket(SocketType.PULL); pullConnect.connect(address); System.out.println("Receiver Started"); pullConnect.recv(0); received.set(true); try { ready.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } pullConnect.close(); context.close(); System.out.println("Receiver Stopped"); } } @Test(timeout = 5000) public void testPollerPollout() throws Exception { int port = Utils.findOpenPort(); String addr = "tcp://127.0.0.1:" + port; Client client = new Client(addr); ZMQ.Context context = ZMQ.context(1); // Socket to send messages to ZMQ.Socket sender = context.socket(SocketType.PUSH); sender.setImmediate(false); sender.bind(addr); ZMQ.Poller outItems = context.poller(); outItems.register(sender, ZMQ.Poller.POLLOUT); ExecutorService executor; try { executor = Executors.newSingleThreadExecutor(); while (!Thread.currentThread().isInterrupted()) { outItems.poll(1000); if (outItems.pollout(0)) { boolean rc = sender.send("OK", 0); assertThat(rc, is(true)); System.out.println("Sender: wrote message"); break; } else { System.out.println("Sender: not writable"); executor.submit(client); } } } finally { client.ready.countDown(); } executor.shutdown(); executor.awaitTermination(4, TimeUnit.SECONDS); outItems.close(); sender.close(); System.out.println("Poller test done"); assertThat(client.received.get(), is(true)); context.term(); } @Test(timeout = 5000) public void testExitPollerIssue580() throws InterruptedException, ExecutionException { Future future; ExecutorService service = Executors.newSingleThreadExecutor(); try ( ZContext context = new ZContext(1); ZMQ.Poller poller = context.createPoller(1)) { ZMQ.Socket socket = context.createSocket(SocketType.PAIR); assertThat(socket, notNullValue()); int rc = poller.register(socket, ZMQ.Poller.POLLIN); assertThat(rc, is(0)); future = service.submit(() -> poller.poll(-1)); assertThat(future, notNullValue()); ZMQ.msleep(100); } service.shutdown(); service.awaitTermination(1, TimeUnit.SECONDS); assertThat(future.get(), is(-1)); } } jeromq-0.6.0/src/test/java/org/zeromq/TestProxy.java000066400000000000000000000176531455771126300224600ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; import org.zeromq.util.ZData; public class TestProxy { static class Client implements Runnable { private final String frontend; private final String name; private final AtomicBoolean result = new AtomicBoolean(); public Client(String name, String frontend) { this.name = name; this.frontend = frontend; } @Override public void run() { Context ctx = ZMQ.context(1); assertThat(ctx, notNullValue()); Socket socket = ctx.socket(SocketType.REQ); boolean rc; rc = socket.setIdentity(id(name)); assertThat(rc, is(true)); System.out.println("Start " + name); Thread.currentThread().setName(name); rc = socket.connect(frontend); assertThat(rc, is(true)); result.set(process(socket)); socket.close(); ctx.close(); System.out.println("Stop " + name); } private boolean process(Socket socket) { boolean rc = socket.send("hello"); if (!rc) { System.out.println(name + " unable to send first message"); return false; } System.out.println(name + " sent 1st message"); String msg = socket.recvStr(0); System.out.println(name + " received " + msg); if (msg == null || !msg.startsWith("OK hello")) { return false; } rc = socket.send("world"); if (!rc) { System.out.println(name + " unable to send second message"); return false; } msg = socket.recvStr(0); System.out.println(name + " received " + msg); return msg != null && msg.startsWith("OK world"); } } static class Dealer implements Runnable { private final String backend; private final String name; private final AtomicBoolean result = new AtomicBoolean(); public Dealer(String name, String backend) { this.name = name; this.backend = backend; } @Override public void run() { Context ctx = ZMQ.context(1); assertThat(ctx, notNullValue()); Thread.currentThread().setName(name); System.out.println("Start " + name); Socket socket = ctx.socket(SocketType.DEALER); boolean rc; rc = socket.setIdentity(id(name)); assertThat(rc, is(true)); rc = socket.connect(backend); assertThat(rc, is(true)); result.set(process(socket)); socket.close(); ctx.close(); System.out.println("Stop " + name); } private boolean process(Socket socket) { int count = 0; while (count < 2) { byte[] msg = socket.recv(0); String msgAsString = new String(msg, ZMQ.CHARSET); if (msg == null || !msgAsString.startsWith("Client-")) { System.out.println(name + " Wrong identity " + msgAsString); return false; } final byte[] identity = msg; System.out.println(name + " received client identity " + ZData.strhex(identity)); msg = socket.recv(0); msgAsString = new String(msg, ZMQ.CHARSET); if (msg == null || msg.length != 0) { System.out.println("Not bottom " + Arrays.toString(msg)); return false; } System.out.println(name + " received bottom " + msgAsString); msg = socket.recv(0); if (msg == null) { System.out.println(name + " Not data " + msg); return false; } msgAsString = new String(msg, ZMQ.CHARSET); System.out.println(name + " received data " + msgAsString); socket.send(identity, ZMQ.SNDMORE); socket.send((byte[]) null, ZMQ.SNDMORE); String response = "OK " + msgAsString + " " + name; socket.send(response, 0); count++; } return true; } } static class Proxy extends Thread { private final String frontend; private final String backend; private final String control; private final AtomicBoolean result = new AtomicBoolean(); Proxy(String frontend, String backend, String control) { this.frontend = frontend; this.backend = backend; this.control = control; } @Override public void run() { Context ctx = ZMQ.context(1); assert (ctx != null); setName("Proxy"); Socket frontend = ctx.socket(SocketType.ROUTER); assertThat(frontend, notNullValue()); frontend.bind(this.frontend); Socket backend = ctx.socket(SocketType.DEALER); assertThat(backend, notNullValue()); backend.bind(this.backend); Socket control = ctx.socket(SocketType.PAIR); assertThat(control, notNullValue()); control.bind(this.control); ZMQ.proxy(frontend, backend, null, control); frontend.close(); backend.close(); control.close(); ctx.close(); result.set(true); } } private static byte[] id(String name) { Random random = new Random(); byte[] id = new byte[10 + random.nextInt(245)]; random.nextBytes(id); System.arraycopy(name.getBytes(ZMQ.CHARSET), 0, id, 0, name.length()); return id; } @Test public void testProxy() throws Exception { String frontend = "tcp://localhost:" + Utils.findOpenPort(); String backend = "tcp://localhost:" + Utils.findOpenPort(); String controlEndpoint = "tcp://localhost:" + Utils.findOpenPort(); Proxy proxy = new Proxy(frontend, backend, controlEndpoint); proxy.start(); ExecutorService executor = Executors.newFixedThreadPool(4); Dealer d1 = new Dealer("Dealer-A", backend); Dealer d2 = new Dealer("Dealer-B", backend); executor.submit(d1); executor.submit(d2); Thread.sleep(1000); Client c1 = new Client("Client-X", frontend); Client c2 = new Client("Client-Y", frontend); executor.submit(c1); executor.submit(c2); executor.shutdown(); executor.awaitTermination(40, TimeUnit.SECONDS); Context ctx = ZMQ.context(1); Socket control = ctx.socket(SocketType.PAIR); control.connect(controlEndpoint); control.send(ZMQ.PROXY_TERMINATE); proxy.join(); control.close(); ctx.close(); assertThat(c1.result.get(), is(true)); assertThat(c2.result.get(), is(true)); assertThat(d1.result.get(), is(true)); assertThat(d2.result.get(), is(true)); assertThat(proxy.result.get(), is(true)); } public void testRepeated() throws Exception { for (int idx = 0; idx < 470; ++idx) { System.out.println("---------- " + idx); testProxy(); Thread.sleep(1000); } } } jeromq-0.6.0/src/test/java/org/zeromq/TestPushPullThreadedTcp.java000066400000000000000000000126141455771126300252130ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.zeromq.ZMQ.Socket; /** * Tests a PUSH-PULL dialog with several methods, each component being on a * separate thread. */ public class TestPushPullThreadedTcp { private static class Worker implements Runnable { private final int count; private final AtomicBoolean finished = new AtomicBoolean(); private int idx; private final Socket receiver; public Worker(Socket receiver, int count) { this.receiver = receiver; this.count = count; } @Override public void run() { idx = 0; while (idx < count) { if (idx % 5000 == 10) { zmq.ZMQ.msleep(100); } ZMsg msg = ZMsg.recvMsg(receiver); msg.destroy(); idx++; } finished.set(true); } } private static class Client implements Runnable { private final Socket sender; private final AtomicBoolean finished = new AtomicBoolean(); private final int count; public Client(Socket sender, int count) { this.sender = sender; this.count = count; } @Override public void run() { int idx = 0; while (idx++ < count) { ZMsg msg = new ZMsg(); msg.add("DATA"); boolean sent = msg.send(sender); assertThat(sent, is(true)); } finished.set(true); } } // @Test public void testRepeated() throws Exception { for (int idx = 0; idx < 2000; ++idx) { System.out.println("+++++++++++ " + idx); testPushPull1(); testPushPull500(); testPushPullWithWatermark(); } } @Test public void testPushPull1() throws Exception { test(1); } @Test public void testPushPull500() throws Exception { System.out.println("Sending 500 messages"); test(500); } @Test public void testPushPullWithWatermark() throws Exception { System.out.println("Sending 20000 messages to trigger watermark limit"); test(20000); } private void test(int count) throws InterruptedException { long start = System.currentTimeMillis(); ExecutorService threadPool = Executors.newFixedThreadPool(2); ZContext ctx = new ZContext(); ZMQ.Socket receiver = ctx.createSocket(SocketType.PULL); assertThat(receiver, notNullValue()); receiver.setImmediate(false); final int port = receiver.bindToRandomPort("tcp://localhost"); ZMQ.Socket sender = ctx.createSocket(SocketType.PUSH); assertThat(sender, notNullValue()); boolean rc = sender.connect("tcp://localhost:" + port); assertThat(rc, is(true)); Worker worker = new Worker(receiver, count); Client client = new Client(sender, count); threadPool.submit(worker); threadPool.submit(client); threadPool.shutdown(); threadPool.awaitTermination(10, TimeUnit.SECONDS); long end = System.currentTimeMillis(); System.out.println("Worker received " + worker.idx + " messages"); assertThat("Unable to send messages", client.finished.get(), is(true)); assertThat("Unable to receive messages", worker.finished.get(), is(true)); receiver.close(); sender.close(); ctx.close(); System.out.println("Test done in " + (end - start) + " millis."); } @Test public void testIssue338() throws InterruptedException, IOException { try ( final ZSocket pull = new ZSocket(SocketType.PULL); final ZSocket push = new ZSocket(SocketType.PUSH)) { final String host = "tcp://localhost:" + Utils.findOpenPort(); pull.bind(host); push.connect(host); final ExecutorService executor = Executors.newFixedThreadPool(1); final int messagesNumber = 300000; Runnable receiver = () -> { String actual = null; int count = messagesNumber; while (count-- > 0) { actual = pull.receiveStringUtf8(); } System.out.println("last message: " + actual); }; executor.submit(receiver); final String expected = "hello"; final long start = System.currentTimeMillis(); for (int idx = 0; idx < messagesNumber; idx++) { push.sendStringUtf8(expected + "_" + idx); } long end = System.currentTimeMillis(); System.out.println("push time :" + (end - start) + " millisec."); executor.shutdown(); executor.awaitTermination(40, TimeUnit.SECONDS); end = System.currentTimeMillis(); System.out.println("all time :" + (end - start) + " millisec."); } } } jeromq-0.6.0/src/test/java/org/zeromq/TestRapidOpenCloseSocket.java000066400000000000000000000033441455771126300253470ustar00rootroot00000000000000package org.zeromq; import java.util.ArrayList; import java.util.List; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQ.Socket; // For issue: https://github.com/zeromq/jeromq/issues/198 public class TestRapidOpenCloseSocket { @Test @Ignore public void testRapidOpenCloseSocket() throws Exception { for (int i = 0; i < 50; i++) { performTest(); } } private void performTest() throws Exception { ZContext ctx = new ZContext(); Socket recvMsgSock = ctx.createSocket(SocketType.PULL); recvMsgSock.bind("tcp://*:" + Utils.findOpenPort()); Socket processMsgSock = ctx.createSocket(SocketType.PUSH); processMsgSock.bind("inproc://process-msg"); List workerSocks = new ArrayList<>(); for (int i = 0; i < 5; i++) { Socket workerSock = ctx.createSocket(SocketType.PULL); workerSock.connect("inproc://process-msg"); workerSocks.add(workerSock); } ZMQQueue queue = new ZMQQueue(ctx.getContext(), recvMsgSock, processMsgSock); Thread proxyThr = new Thread(queue); proxyThr.setName("Proxy thr"); proxyThr.start(); for (final Socket workerSock : workerSocks) { Thread workerThr = new Thread(() -> { try { while (true) { byte[] msg = workerSock.recv(); // Process the msg! } } catch (Exception e) { } }); workerThr.setName("A worker thread"); workerThr.start(); } try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } System.out.println("Closing now"); ctx.destroy(); System.out.println("Successfully closed"); } } jeromq-0.6.0/src/test/java/org/zeromq/TestReqRouterThreadedTcp.java000066400000000000000000000120651455771126300253670ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.zeromq.ZMQ.Socket; /** * Tests a REQ-ROUTER dialog with several methods, * each component being on a separate thread. * */ public class TestReqRouterThreadedTcp { private static final long REQUEST_TIMEOUT = 1000; // msecs /** * A very simple server for one reply only. * */ private static class Server implements Runnable { private final int port; /** * Creates a new server. * @param port the port to which to connect. */ public Server(int port) { this.port = port; } @Override public void run() { ZContext ctx = new ZContext(); ZMQ.Socket server = ctx.createSocket(SocketType.ROUTER); server.bind("tcp://localhost:" + port); System.out.println("Server started"); ZMsg msg = ZMsg.recvMsg(server); // only one echo message for this server msg.send(server); System.out.println("Server sent reply"); zmq.ZMQ.sleep(1); msg.destroy(); // Clean up. server.close(); ctx.close(); } } private static class Client implements Runnable { private final int port; final AtomicBoolean finished = new AtomicBoolean(); /** * Creates a new client. * @param port the port to which to connect. */ public Client(int port) { this.port = port; } @Override public void run() { ZContext ctx = new ZContext(); ZMQ.Socket client = ctx.createSocket(SocketType.REQ); client.connect("tcp://localhost:" + port); System.out.println("Client started"); client.send("DATA"); System.out.println("Client sent message"); inBetween(ctx, client); String reply = client.recvStr(); System.out.println("Client received message"); assertThat(reply, notNullValue()); assertThat(reply, is("DATA")); finished.set(true); // Clean up. client.close(); ctx.close(); } /** * Called between the request-reply cycle. * @param client the socket participating to the cycle of request-reply */ protected void inBetween(ZContext ctx, Socket client) { // to be overriden } } private static class ClientPoll extends Client { public ClientPoll(int port) { super(port); } /** * Here we use a poller to check for readability of the message. * This should activate the prefetching mechanism. */ @Override protected void inBetween(ZContext ctx, Socket client) { // Poll socket for a reply, with timeout ZMQ.Poller poller = ctx.createPoller(1); poller.register(client, ZMQ.Poller.POLLIN); int rc = poller.poll(REQUEST_TIMEOUT); assertThat(rc, is(1)); boolean readable = poller.pollin(0); assertThat(readable, is(true)); // now a message should have been prefetched poller.close(); } } /** * Test dialog directly. * @throws Exception if something bad occurs. */ @Test public void testReqRouterTcp() throws Exception { System.out.println("test Req + Router"); int port = Utils.findOpenPort(); ExecutorService executor = Executors.newFixedThreadPool(2); Server server = new Server(port); Client client = new Client(port); executor.submit(server); executor.submit(client); executor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS); boolean finished = client.finished.get(); assertThat(finished, is(true)); } /** * Test dialog with a polling access in between request-reply. * This should activate the prefetching mechanism. * @throws Exception if something bad occurs. */ @Test public void testReqRouterTcpPoll() throws Exception { System.out.println("test Req + Router with polling"); int port = Utils.findOpenPort(); ExecutorService executor = Executors.newFixedThreadPool(2); Server server = new Server(port); ClientPoll client = new ClientPoll(port); executor.submit(server); executor.submit(client); executor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS); boolean finished = client.finished.get(); assertThat(finished, is(true)); } } jeromq-0.6.0/src/test/java/org/zeromq/TestSocketStreams.java000066400000000000000000000025521455771126300241160ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import org.junit.Ignore; import org.junit.Test; @Ignore public class TestSocketStreams { @Test public void testRecvStream() throws IOException { int port = Utils.findOpenPort(); try ( final ZMQ.Context ctx = new ZMQ.Context(1); final ZMQ.Socket pull = ctx.socket(SocketType.PULL); final ZMQ.Socket push = ctx.socket(SocketType.PUSH)) { pull.bind("tcp://*:" + port); push.connect("tcp://127.0.0.1:" + port); final byte[] expected = new byte[]{0x11, 0x22, 0x33}; push.send(expected); } } @Test public void testRecvStrStream() throws IOException { int port = Utils.findOpenPort(); try ( final ZMQ.Context ctx = new ZMQ.Context(1); final ZMQ.Socket pull = ctx.socket(SocketType.PULL); final ZMQ.Socket push = ctx.socket(SocketType.PUSH)) { pull.bind("tcp://*:" + port); push.connect("tcp://127.0.0.1:" + port); final String expected = "Hello"; push.send(expected); // Optional first = pull.recvStrStream().peek(System.out::print).findFirst(); // assertTrue(first.isPresent()); // assertEquals(expected, first.get()); } } } jeromq-0.6.0/src/test/java/org/zeromq/TestZActor.java000066400000000000000000000111021455771126300225200ustar00rootroot00000000000000package org.zeromq; import java.util.Collections; import java.util.List; import java.util.UUID; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZActor.Actor; import org.zeromq.ZMQ.Socket; public class TestZActor { @Test public void testMinimalistic() { final Actor acting = new ZActor.SimpleActor() { @Override public List createSockets(ZContext ctx, Object... args) { assert ("TEST".equals(args[0])); return Collections.singletonList(ctx.createSocket(SocketType.PUB)); } @Override public boolean backstage(Socket pipe, ZPoller poller, int events) { String string = pipe.recvStr(); if ("HELLO".equals(string)) { pipe.send("WORLD"); } if ("QUIT".equals(string)) { pipe.send("EXIT"); return false; } return true; } }; final ZContext context = new ZContext(); final ZActor.Duo duo = new ZActor.Duo(acting, new ZActor.SimpleActor()); final ZActor actor = new ZActor(context, duo, "LOCK", Collections.singletonList("TEST").toArray()); final Socket pipe = actor.pipe(); boolean rc = pipe.send("HELLO"); Assert.assertTrue("Unable to send a message through pipe", rc); ZMsg msg = actor.recv(); String world = msg.popString(); Assert.assertEquals("No matching response from actor", "WORLD", world); rc = pipe.send("QUIT"); msg = actor.recv(); Assert.assertNotNull("Unable to receive EXIT message", msg); msg = actor.recv(); Assert.assertNull("Able to receive a message from a locked actor", msg); rc = actor.sign(); Assert.assertFalse("Locked actor is still here", rc); rc = actor.send("whatever"); Assert.assertFalse("Able to send a message to a locked actor", rc); context.close(); System.out.println("."); } @Test public void testRecreateAgent() { ZActor.Actor acting = new ZActor.SimpleActor() { private int counter = 0; @Override public List createSockets(ZContext ctx, Object... args) { ++counter; System.out.print(".Acting Ready for a hello world."); assert ("TEST".equals(args[0])); return super.createSockets(ctx, args); } @Override public boolean backstage(Socket pipe, ZPoller poller, int events) { String string = pipe.recvStr(); if ("HELLO".equals(string)) { System.out.print("Hi! "); pipe.send("WORLD", ZMQ.SNDMORE); pipe.send(Integer.toString(counter)); return false; } return true; } @Override public boolean destroyed(ZContext ctx, Socket pipe, ZPoller poller) { if (counter == 2) { System.out.print(".Acting Finished."); return false; } // recreate a new agent return true; } }; ZContext context = new ZContext(); ZActor actor = new ZActor(context, acting, UUID.randomUUID().toString(), Collections.singletonList("TEST").toArray()); ZAgent agent = actor.agent(); agent = actor.agent(); Socket pipe = agent.pipe(); boolean rc = pipe.send("HELLO"); assert (rc); ZMsg msg = actor.recv(); String world = msg.popString(); String counter = msg.popString(); assert ("WORLD".equals(world)); assert ("1".equals(counter)); rc = actor.send("HELLO"); assert (rc); msg = agent.recv(); Assert.assertNotNull("unable to receive a message from an actor", msg); world = msg.popString(); counter = msg.popString(); assert (msg != null); assert ("WORLD".equals(world)); assert ("2".equals(counter)); msg = agent.recv(); Assert.assertNull("Able to receive a message from a locked actor", msg); rc = agent.sign(); Assert.assertFalse("Locked actor is still here", rc); rc = agent.send("whatever"); Assert.assertFalse("Able to send a message to a locked actor", rc); context.close(); System.out.println(); } } jeromq-0.6.0/src/test/java/org/zeromq/TestZCancellationToken.java000066400000000000000000000067621455771126300250650ustar00rootroot00000000000000package org.zeromq; import org.junit.Assert; import org.junit.Test; import zmq.ZError; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class TestZCancellationToken { @Test public void cancelReceiveThreadSafe() { try (ZContext context = new ZContext(); ZMQ.Socket socket = context.createSocket(SocketType.CLIENT)) { ZMQ.CancellationToken cancellationToken = socket.createCancellationToken(); Thread t = new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } cancellationToken.cancel(); }); t.start(); try { socket.recv(0, cancellationToken); Assert.fail(); } catch (ZMQException ex) { assertThat(ex.getErrorCode(), is(ZError.ECANCELED)); } } } @Test public void cancelSendThreadSafe() { try (ZContext context = new ZContext(); ZMQ.Socket socket = context.createSocket(SocketType.CLIENT)) { ZMQ.CancellationToken cancellationToken = socket.createCancellationToken(); Thread t = new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } cancellationToken.cancel(); }); t.start(); try { socket.send(new byte[1], 0, cancellationToken); Assert.fail(); } catch (ZMQException ex) { assertThat(ex.getErrorCode(), is(ZError.ECANCELED)); } } } @Test public void cancelReceive() { try (ZContext context = new ZContext(); ZMQ.Socket socket = context.createSocket(SocketType.DEALER)) { ZMQ.CancellationToken cancellationToken = socket.createCancellationToken(); Thread t = new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } cancellationToken.cancel(); }); t.start(); try { socket.recv(0, cancellationToken); Assert.fail(); } catch (ZMQException ex) { assertThat(ex.getErrorCode(), is(ZError.ECANCELED)); } } } @Test public void cancelSend() { try (ZContext context = new ZContext(); ZMQ.Socket socket = context.createSocket(SocketType.DEALER)) { ZMQ.CancellationToken cancellationToken = socket.createCancellationToken(); Thread t = new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } cancellationToken.cancel(); }); t.start(); try { socket.send(new byte[1], 0, cancellationToken); Assert.fail(); } catch (ZMQException ex) { assertThat(ex.getErrorCode(), is(ZError.ECANCELED)); } } } } jeromq-0.6.0/src/test/java/org/zeromq/TestZContext.java000066400000000000000000000123011455771126300230760ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class TestZContext { @Test(timeout = 5000) public void testZContext() { ZContext ctx = new ZContext(); ctx.createSocket(SocketType.PAIR); ctx.createSocket(SocketType.REQ); ctx.createSocket(SocketType.REP); ctx.createSocket(SocketType.PUB); ctx.createSocket(SocketType.SUB); ctx.close(); assertThat(ctx.getSockets().isEmpty(), is(true)); } @Test(timeout = 5000) public void testZContextSocketCloseBeforeContextClose() { ZContext ctx = new ZContext(); Socket s1 = ctx.createSocket(SocketType.PUSH); Socket s2 = ctx.createSocket(SocketType.PULL); s1.close(); s2.close(); ctx.close(); } @Test(timeout = 5000) public void testZContextLinger() { try (ZContext ctx = new ZContext()) { ctx.setLinger(125); Socket s = ctx.createSocket(SocketType.PUSH); assertThat(ctx.getLinger(), is(125)); assertThat(s.getLinger(), is(125)); } } @Test(timeout = 5000) public void testConstruction() { ZContext ctx = new ZContext(); assertThat(ctx, notNullValue()); assertThat(ctx.getContext(), notNullValue()); assertThat(ctx.isClosed(), is(false)); assertThat(ctx.getIoThreads(), is(1)); assertThat(ctx.getLinger(), is(0)); assertThat(ctx.isMain(), is(true)); } @SuppressWarnings("deprecation") @Test(timeout = 5000) public void testDestruction() { ZContext ctx = new ZContext(); ctx.close(); assertThat(ctx.isClosed(), is(true)); assertThat(ctx.getSockets().isEmpty(), is(true)); // Ensure context is not destroyed if not in main thread ZContext ctx1 = new ZContext(); ctx1.setMain(false); @SuppressWarnings("unused") Socket s = ctx1.createSocket(SocketType.PAIR); ctx1.close(); assertThat(ctx1.getSockets().isEmpty(), is(true)); assertThat(ctx1.getContext(), notNullValue()); } @Test(timeout = 5000) public void testAddingSockets() throws ZMQException { try (ZContext ctx = new ZContext()) { Socket s = ctx.createSocket(SocketType.PUB); assertThat(s, notNullValue()); assertThat(s.getSocketType(), is(SocketType.PUB)); Socket s1 = ctx.createSocket(SocketType.REQ); assertThat(s1, notNullValue()); assertThat(s1.getSocketType(), is(SocketType.REQ)); assertThat(ctx.getSockets().size(), is(2)); } } @Test(timeout = 5000) public void testRemovingSockets() throws ZMQException { ZContext ctx = new ZContext(); try (Socket s = ctx.createSocket(SocketType.PUB)) { assertThat(s, notNullValue()); assertThat(ctx.getSockets().size(), is(1)); } finally { assertThat(ctx.getSockets().size(), is(0)); ctx.close(); } } @Test(timeout = 5000) public void testShadow() { ZContext ctx = new ZContext(); // A socket that will be forgotten at the parent Socket s = ctx.createSocket(SocketType.SUB); Assert.assertNotNull(s); Assert.assertEquals(1, ctx.getSockets().size()); // A shadow that will be forgotten ZContext shadowCtx0 = ctx.shadow(); // A socket that will be forgotten in a shadow @SuppressWarnings("unused") Socket s0 = shadowCtx0.createSocket(SocketType.PUB); try (ZContext shadowCtx1 = ctx.shadow()) { Assert.assertEquals(0, shadowCtx1.getSockets().size()); try (Socket s1 = shadowCtx1.createSocket(SocketType.PUB)) { Assert.assertEquals(1, ctx.getSockets().size()); Assert.assertEquals(1, shadowCtx1.getSockets().size()); } } ctx.close(); Assert.assertTrue(ctx.isEmpty()); } @Test(timeout = 5000) public void testSeveralPendingInprocSocketsAreClosedIssue595() { ZContext ctx = new ZContext(); for (SocketType type : SocketType.values()) { for (int idx = 0; idx < 3; ++idx) { Socket socket = ctx.createSocket(type); assertThat(socket, notNullValue()); boolean rc = socket.connect("inproc://" + type.name()); assertThat(rc, is(true)); } } ctx.close(); assertThat(ctx.isClosed(), is(true)); } @Test(timeout = 1000) public void testInterruptedClose() throws Throwable { ZContext ctx = new ZContext(); Socket s = ctx.createSocket(SocketType.PULL); int port = Utils.findOpenPort(); s.bind("tcp://*:" + port); Thread.currentThread().interrupt(); s.close(); ZMQException ex = Assert.assertThrows(ZMQException.class, ctx::close); assertThat(ex.getErrorCode(), is(4)); assertThat(Thread.interrupted(), is(false)); } } jeromq-0.6.0/src/test/java/org/zeromq/TestZLoop.java000066400000000000000000000201121455771126300223620ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZLoop.IZLoopHandler; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; public class TestZLoop { private String received; private ZContext ctx; private Socket input; private Socket output; @Before public void setUp() { ctx = new ZContext(); assert (ctx != null); output = ctx.createSocket(SocketType.PAIR); assert (output != null); output.bind("inproc://zloop.test"); input = ctx.createSocket(SocketType.PAIR); assert (input != null); input.connect("inproc://zloop.test"); received = "FAILED"; } @After public void tearDown() { ctx.destroy(); } @Ignore public void testZLoopWithUDP() throws IOException { final int port = Utils.findOpenPort(); final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", port); ZLoop loop = new ZLoop(ctx); DatagramChannel udpIn = DatagramChannel.open(); assertThat(udpIn, notNullValue()); udpIn.configureBlocking(false); udpIn.socket().bind(new InetSocketAddress(port)); DatagramChannel udpOut = DatagramChannel.open(); assertThat(udpOut, notNullValue()); udpOut.configureBlocking(false); udpOut.socket().connect(addr); final AtomicInteger counter = new AtomicInteger(); final AtomicBoolean done = new AtomicBoolean(); loop.addPoller(new PollItem(udpIn, ZMQ.Poller.POLLIN), (loop12, item, arg) -> { DatagramChannel udpIn1 = (DatagramChannel) arg; ByteBuffer bb = ByteBuffer.allocate(3); try { udpIn1.receive(bb); String read = new String(bb.array(), 0, bb.limit(), ZMQ.CHARSET); assertThat(read, is("udp")); done.set(true); counter.incrementAndGet(); } catch (IOException e) { e.printStackTrace(); fail(); } return -1; }, udpIn); loop.addPoller(new PollItem(udpOut, ZMQ.Poller.POLLOUT), (loop1, item, arg) -> { DatagramChannel udpOut1 = (DatagramChannel) arg; try { ByteBuffer bb = ByteBuffer.allocate(3); bb.put("udp".getBytes(ZMQ.CHARSET)); bb.flip(); int written = udpOut1.send(bb, addr); assertThat(written, is(3)); counter.incrementAndGet(); } catch (IOException e) { e.printStackTrace(); fail(); } return 0; }, udpOut); loop.start(); assertThat(done.get(), is(true)); assertThat(counter.get(), is(2)); udpIn.close(); udpOut.close(); } @Test public void testZLoop() { int rc; // setUp() should create the context assert (ctx != null); ZLoop loop = new ZLoop(ctx); assert (loop != null); ZLoop.IZLoopHandler timerEvent = (loop12, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop1, item, arg) -> { received = ((Socket) arg).recvStr(0); // Just end the reactor return -1; }; // After 10 msecs, send a ping message to output loop.addTimer(10, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); Assert.assertEquals(0, rc); loop.start(); loop.removePoller(pollInput); Assert.assertEquals("PING", received); } @Test public void testZLoopAddTimerFromTimer() { int rc; ZLoop loop = new ZLoop(ctx); assert (loop != null); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { final long now = System.currentTimeMillis(); IZLoopHandler timerEvent2 = (loop12, item1, arg1) -> { final long now2 = System.currentTimeMillis(); assert (now2 >= now + 10); ((Socket) arg1).send("PING", 0); return 0; }; loop13.addTimer(10, 1, timerEvent2, arg); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop1, item, arg) -> { received = ((Socket) arg).recvStr(0); // Just end the reactor return -1; }; // After 10 msecs, fire a timer that registers // another timer that sends the ping message loop.addTimer(10, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); Assert.assertEquals(0, rc); loop.start(); loop.removePoller(pollInput); Assert.assertEquals("PING", received); } @Test(timeout = 1000) public void testZLoopAddTimerFromSocketHandler() { int rc; ZLoop loop = new ZLoop(ctx); assert (loop != null); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop12, item, arg) -> { final long now = System.currentTimeMillis(); IZLoopHandler timerEvent2 = (loop1, item1, arg1) -> { final long now2 = System.currentTimeMillis(); Assert.assertTrue(now2 >= now + 10); received = ((Socket) arg1).recvStr(0); // Just end the reactor return -1; }; // After 10 msec fire a timer that ends the reactor loop12.addTimer(10, 1, timerEvent2, arg); return 0; }; // Fire a timer that sends the ping message loop.addTimer(0, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); Assert.assertEquals(0, rc); loop.start(); loop.removePoller(pollInput); Assert.assertEquals("PING", received); } @Test(timeout = 1000) public void testZLoopEndReactorFromTimer() { int rc; ZLoop loop = new ZLoop(ctx); assert (loop != null); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop12, item, arg) -> { // After 10 msecs, fire an event that ends the reactor IZLoopHandler shutdownEvent = (loop1, item1, arg1) -> { received = ((Socket) arg1).recvStr(0); // Just end the reactor return -1; }; loop12.addTimer(10, 1, shutdownEvent, arg); return 0; }; // Fire event that sends a ping message to output loop.addTimer(0, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); Assert.assertEquals(0, rc); loop.start(); loop.removePoller(pollInput); Assert.assertEquals("PING", received); } } jeromq-0.6.0/src/test/java/org/zeromq/TestZMQ.java000066400000000000000000001067511455771126300220040ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.IOException; import java.util.Arrays; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMQ.Socket.Mechanism; import zmq.ZError; import zmq.msg.MsgAllocator; import zmq.msg.MsgAllocatorDirect; import zmq.util.Errno; public class TestZMQ { private Context ctx; @Before public void setUp() { ctx = ZMQ.context(1); assertThat(ctx, notNullValue()); new Errno().set(0); } @After public void tearDown() { ctx.close(); } @Test public void testErrno() { Socket socket = ctx.socket(SocketType.DEALER); assertThat(socket.errno(), is(0)); socket.close(); } @Test(expected = ZMQException.class) public void testBindSameAddress() throws IOException { int port = Utils.findOpenPort(); ZMQ.Context context = ZMQ.context(1); ZMQ.Socket socket1 = context.socket(SocketType.REQ); ZMQ.Socket socket2 = context.socket(SocketType.REQ); socket1.bind("tcp://*:" + port); try { socket2.bind("tcp://*:" + port); fail("Exception not thrown"); } catch (ZMQException e) { assertEquals(e.getErrorCode(), ZMQ.Error.EADDRINUSE.getCode()); throw e; } finally { socket1.close(); socket2.close(); context.term(); } } @Test(expected = ZMQException.class) public void testBindInprocSameAddress() { ZMQ.Context context = ZMQ.context(1); ZMQ.Socket socket1 = context.socket(SocketType.REQ); ZMQ.Socket socket2 = context.socket(SocketType.REQ); socket1.bind("inproc://address.already.in.use"); socket2.bind("inproc://address.already.in.use"); assertThat(socket2.errno(), is(ZError.EADDRINUSE)); socket1.close(); socket2.close(); context.term(); } @Test public void testSocketUnbind() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); System.out.println("Connecting socket to unbind on port " + port); byte[] data = "ABC".getBytes(); rc = push.send(data); assertThat(rc, is(true)); assertArrayEquals(data, pull.recv()); rc = pull.unbind("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); rc = push.send(data); assertThat(rc, is(true)); assertNull(pull.recv()); push.close(); pull.close(); context.term(); } @Test public void testSocketSendRecvArray() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); byte[] data = "ABC".getBytes(ZMQ.CHARSET); rc = push.sendMore("DEF"); assertThat(rc, is(true)); rc = push.send(data, 0, data.length - 1, 0); assertThat(rc, is(true)); byte[] recvd = pull.recv(); assertThat(recvd, is("DEF".getBytes(ZMQ.CHARSET))); byte[] datb = new byte[2]; int received = pull.recv(datb, 0, datb.length, 0); assertThat(received, is(2)); assertThat(datb, is("AB".getBytes(ZMQ.CHARSET))); push.close(); pull.close(); context.term(); } @Test public void testSocketSendRecvPicture() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); String picture = "1248sbfzm"; ZMsg msg = new ZMsg(); msg.add("Hello"); msg.add("World"); rc = push.sendPicture( picture, 255, 65535, 429496729, Long.MAX_VALUE, "Hello World", "ABC".getBytes(ZMQ.CHARSET), new ZFrame("My frame"), msg); assertThat(rc, is(true)); Object[] objects = pull.recvPicture(picture); assertThat(objects[0], is(equalTo(255))); assertThat(objects[1], is(equalTo(65535))); assertThat(objects[2], is(equalTo(429496729))); assertThat(objects[3], is(equalTo(Long.MAX_VALUE))); assertThat(objects[4], is(equalTo("Hello World"))); assertThat(objects[5], is(equalTo("ABC".getBytes(zmq.ZMQ.CHARSET)))); assertThat(objects[6], is(equalTo(new ZFrame("My frame")))); assertThat(objects[7], is(equalTo(new ZFrame()))); ZMsg expectedMsg = new ZMsg(); expectedMsg.add("Hello"); expectedMsg.add("World"); assertThat(objects[8], is(equalTo(expectedMsg))); push.close(); pull.close(); context.term(); } @Test public void testSocketSendRecvBinaryPicture() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); String picture = "1248sScfm"; ZMsg msg = new ZMsg(); msg.add("Hello"); msg.add("World"); rc = push.sendBinaryPicture( picture, 255, 65535, 429496729, Long.MAX_VALUE, "Hello World", "Hello cruel World!", "ABC".getBytes(ZMQ.CHARSET), new ZFrame("My frame"), msg); assertThat(rc, is(true)); Object[] objects = pull.recvBinaryPicture(picture); assertThat(objects[0], is(equalTo(255))); assertThat(objects[1], is(equalTo(65535))); assertThat(objects[2], is(equalTo(429496729))); assertThat(objects[3], is(equalTo(Long.MAX_VALUE))); assertThat(objects[4], is(equalTo("Hello World"))); assertThat(objects[5], is(equalTo("Hello cruel World!"))); assertThat(objects[6], is(equalTo("ABC".getBytes(zmq.ZMQ.CHARSET)))); assertThat(objects[7], is(equalTo(new ZFrame("My frame")))); ZMsg expectedMsg = new ZMsg(); expectedMsg.add("Hello"); expectedMsg.add("World"); assertThat(objects[8], is(equalTo(expectedMsg))); push.close(); pull.close(); context.term(); } @Test public void testContextBlocky() { Socket router = ctx.socket(SocketType.ROUTER); long rc = router.getLinger(); assertThat(rc, is(-1L)); router.close(); ctx.setBlocky(false); router = ctx.socket(SocketType.ROUTER); rc = router.getLinger(); assertThat(rc, is(0L)); router.close(); } @Test(timeout = 1000) public void testSocketDoubleClose() { Socket socket = ctx.socket(SocketType.PUSH); socket.close(); socket.close(); } @Test public void testSubscribe() { ZMQ.Socket socket = ctx.socket(SocketType.SUB); boolean rc = socket.subscribe("abc"); assertThat(rc, is(true)); rc = socket.unsubscribe("abc"); assertThat(rc, is(true)); rc = socket.unsubscribe("abc".getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketAffinity() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); socket.setAffinity(42); long rc = socket.getAffinity(); assertThat(rc, is(42L)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketBacklog() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setBacklog(42L); assertThat(set, is(true)); int rc = socket.getBacklog(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketConflate() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setConflate(true); assertThat(set, is(true)); boolean rc = socket.getConflate(); assertThat(rc, is(true)); set = socket.setConflate(false); assertThat(set, is(true)); rc = socket.isConflate(); assertThat(rc, is(false)); socket.close(); } @Test public void testSocketConnectRid() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); boolean set = socket.setConnectRid("rid"); assertThat(set, is(true)); set = socket.setConnectRid("rid".getBytes(ZMQ.CHARSET)); assertThat(set, is(true)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketCurveAsServer() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setCurveServer(true); assertThat(rc, is(true)); boolean server = socket.getCurveServer(); assertThat(server, is(true)); server = socket.getAsServerCurve(); assertThat(server, is(true)); server = socket.isAsServerCurve(); assertThat(server, is(true)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.CURVE)); socket.close(); } @Test public void testSocketCurveSecret() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); byte[] key = new byte[32]; Arrays.fill(key, (byte) 0x2); boolean rc = socket.setCurveSecretKey(key); assertThat(rc, is(true)); byte[] curve = socket.getCurveSecretKey(); assertThat(curve, is(key)); boolean server = socket.getCurveServer(); assertThat(server, is(false)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.CURVE)); socket.close(); } @Test public void testSocketCurvePublic() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.NULL)); byte[] key = new byte[32]; Arrays.fill(key, (byte) 0x2); boolean rc = socket.setCurvePublicKey(key); assertThat(rc, is(true)); byte[] curve = socket.getCurvePublicKey(); assertThat(curve, is(key)); boolean server = socket.getCurveServer(); assertThat(server, is(false)); mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.CURVE)); socket.close(); } @Test public void testSocketCurveServer() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); byte[] key = new byte[32]; Arrays.fill(key, (byte) 0x2); boolean rc = socket.setCurveServerKey(key); assertThat(rc, is(true)); byte[] curve = socket.getCurveServerKey(); assertThat(curve, is(key)); boolean server = socket.getCurveServer(); assertThat(server, is(false)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.CURVE)); socket.close(); } @Test public void testSocketHandshake() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setHandshakeIvl(42); assertThat(set, is(true)); int rc = socket.getHandshakeIvl(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketHeartbeatIvl() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setHeartbeatIvl(42); assertThat(set, is(true)); int rc = socket.getHeartbeatIvl(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketHeartbeatTtl() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setHeartbeatTtl(420); assertThat(set, is(true)); int rc = socket.getHeartbeatTtl(); assertThat(rc, is(400)); socket.close(); } @Test public void testSocketHeartbeatTimeout() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setHeartbeatTimeout(42); assertThat(set, is(true)); int rc = socket.getHeartbeatTimeout(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketHeartbeatContext() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); byte[] context = new byte[3]; context[0] = 4; context[1] = 2; context[2] = 1; boolean set = socket.setHeartbeatContext(context); assertThat(set, is(true)); byte[] hctx = socket.getHeartbeatContext(); assertThat(hctx, is(context)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketHWM() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setHWM(42); assertThat(set, is(true)); int rc = socket.getRcvHWM(); assertThat(rc, is(42)); rc = socket.getSndHWM(); assertThat(rc, is(42)); set = socket.setHWM(43L); assertThat(set, is(true)); rc = socket.getRcvHWM(); assertThat(rc, is(43)); rc = socket.getSndHWM(); assertThat(rc, is(43)); rc = socket.getHWM(); assertThat(rc, is(-1)); socket.close(); } @Test public void testSocketIdentity() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); byte[] identity = new byte[42]; Arrays.fill(identity, (byte) 0x42); boolean set = socket.setIdentity(identity); assertThat(set, is(true)); byte[] rc = socket.getIdentity(); assertThat(rc, is(identity)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketImmediate() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setImmediate(false); assertThat(set, is(true)); boolean rc = socket.getImmediate(); assertThat(rc, is(false)); rc = socket.getDelayAttachOnConnect(); assertThat(rc, is(true)); set = socket.setImmediate(true); assertThat(set, is(true)); rc = socket.getImmediate(); assertThat(rc, is(true)); rc = socket.getDelayAttachOnConnect(); assertThat(rc, is(false)); set = socket.setDelayAttachOnConnect(false); assertThat(set, is(true)); rc = socket.getImmediate(); assertThat(rc, is(true)); rc = socket.getDelayAttachOnConnect(); assertThat(rc, is(false)); set = socket.setDelayAttachOnConnect(true); assertThat(set, is(true)); rc = socket.getImmediate(); assertThat(rc, is(false)); rc = socket.getDelayAttachOnConnect(); assertThat(rc, is(true)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketIPv6() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setIPv6(true); assertThat(set, is(true)); boolean rc = socket.getIPv6(); assertThat(rc, is(true)); rc = socket.getIPv4Only(); assertThat(rc, is(false)); set = socket.setIPv6(false); assertThat(set, is(true)); rc = socket.getIPv6(); assertThat(rc, is(false)); rc = socket.getIPv4Only(); assertThat(rc, is(true)); set = socket.setIPv4Only(false); assertThat(set, is(true)); rc = socket.getIPv6(); assertThat(rc, is(true)); rc = socket.getIPv4Only(); assertThat(rc, is(false)); set = socket.setIPv4Only(true); assertThat(set, is(true)); rc = socket.getIPv6(); assertThat(rc, is(false)); rc = socket.getIPv4Only(); assertThat(rc, is(true)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketLinger() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean set = socket.setLinger(42); assertThat(set, is(true)); int rc = socket.getLinger(); assertThat(rc, is(42)); set = socket.setLinger(42L); assertThat(set, is(true)); rc = socket.getLinger(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketMaxMsgSize() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); boolean set = socket.setMaxMsgSize(42); assertThat(set, is(true)); long rc = socket.getMaxMsgSize(); assertThat(rc, is(42L)); socket.close(); } @Test public void testSocketMsgAllocationThreshold() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); boolean set = socket.setMsgAllocationHeapThreshold(42); assertThat(set, is(true)); int rc = socket.getMsgAllocationHeapThreshold(); assertThat(rc, is(42)); socket.close(); } @Test public void testSocketMsgAllocator() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); MsgAllocator allocator = new MsgAllocatorDirect(); boolean set = socket.setMsgAllocator(allocator); assertThat(set, is(true)); // TODO socket.close(); } @Test(expected = UnsupportedOperationException.class) public void testSocketMulticastHops() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); try { socket.setMulticastHops(42); } finally { socket.close(); } } @Test public void testSocketGetMulticastHops() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); long rc = socket.getMulticastHops(); assertThat(rc, is(1L)); socket.close(); } @SuppressWarnings("deprecation") @Test(expected = UnsupportedOperationException.class) public void testSocketMulticastLoop() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); try { socket.setMulticastLoop(true); } finally { socket.close(); } } @SuppressWarnings("deprecation") @Test public void testSocketHasMulticastLoop() { final Socket socket = ctx.socket(SocketType.STREAM); assertThat(socket, notNullValue()); boolean rc = socket.hasMulticastLoop(); assertThat(rc, is(false)); socket.close(); } @Test public void testSocketPlainPassword() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setPlainPassword("password"); assertThat(rc, is(true)); String password = socket.getPlainPassword(); assertThat(password, is("password")); boolean server = socket.getPlainServer(); assertThat(server, is(false)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.PLAIN)); socket.close(); } @Test public void testSocketPlainUsername() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setPlainUsername("username"); assertThat(rc, is(true)); String username = socket.getPlainUsername(); assertThat(username, is("username")); boolean server = socket.getPlainServer(); assertThat(server, is(false)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.PLAIN)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketPlainServer() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setPlainServer(true); assertThat(rc, is(true)); boolean server = socket.getPlainServer(); assertThat(server, is(true)); server = socket.isAsServerPlain(); assertThat(server, is(true)); server = socket.getAsServerPlain(); assertThat(server, is(true)); Mechanism mechanism = socket.getMechanism(); assertThat(mechanism, is(Mechanism.PLAIN)); socket.close(); } @Test public void testSocketProbeRouter() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); boolean rc = socket.setProbeRouter(true); assertThat(rc, is(true)); socket.close(); } @Test(expected = UnsupportedOperationException.class) public void testSocketRate() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); try { socket.setRate(42); } finally { socket.close(); } } @Test public void testSocketGetRate() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); long rate = socket.getRate(); assertThat(rate, is(100L)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketRcvHwm() { final Socket socket = ctx.socket(SocketType.DEALER); assertThat(socket, notNullValue()); boolean rc = socket.setRcvHWM(42L); assertThat(rc, is(true)); int hwm = socket.getRcvHWM(); assertThat(hwm, is(42)); hwm = socket.getSndHWM(); assertThat(hwm, is(not(42))); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketSndHwm() { final Socket socket = ctx.socket(SocketType.DEALER); assertThat(socket, notNullValue()); boolean rc = socket.setSndHWM(42L); assertThat(rc, is(true)); int hwm = socket.getSndHWM(); assertThat(hwm, is(42)); hwm = socket.getRcvHWM(); assertThat(hwm, is(not(42))); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketReceiveBufferSize() { final Socket socket = ctx.socket(SocketType.DEALER); assertThat(socket, notNullValue()); boolean rc = socket.setReceiveBufferSize(42L); assertThat(rc, is(true)); int size = socket.getReceiveBufferSize(); assertThat(size, is(42)); size = socket.getSendBufferSize(); assertThat(size, is(not(42))); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketSendBufferSize() { final Socket socket = ctx.socket(SocketType.DEALER); assertThat(socket, notNullValue()); boolean rc = socket.setSendBufferSize(42L); assertThat(rc, is(true)); int size = socket.getSendBufferSize(); assertThat(size, is(42)); size = socket.getReceiveBufferSize(); assertThat(size, is(not(42))); socket.close(); } @Test public void testSocketReceiveTimeOut() { final Socket socket = ctx.socket(SocketType.PAIR); assertThat(socket, notNullValue()); boolean rc = socket.setReceiveTimeOut(42); assertThat(rc, is(true)); int size = socket.getReceiveTimeOut(); assertThat(size, is(42)); size = socket.getSendTimeOut(); assertThat(size, is(not(42))); socket.close(); } @Test public void testSocketSendTimeOut() { final Socket socket = ctx.socket(SocketType.PAIR); assertThat(socket, notNullValue()); boolean rc = socket.setSendTimeOut(42); assertThat(rc, is(true)); int size = socket.getSendTimeOut(); assertThat(size, is(42)); size = socket.getReceiveTimeOut(); assertThat(size, is(not(42))); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketReconnectIVL() { final Socket socket = ctx.socket(SocketType.REP); assertThat(socket, notNullValue()); boolean rc = socket.setReconnectIVL(42L); assertThat(rc, is(true)); int reconnect = socket.getReconnectIVL(); assertThat(reconnect, is(42)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketReconnectIVLMax() { final Socket socket = ctx.socket(SocketType.REP); assertThat(socket, notNullValue()); boolean rc = socket.setReconnectIVLMax(42L); assertThat(rc, is(true)); int reconnect = socket.getReconnectIVLMax(); assertThat(reconnect, is(42)); socket.close(); } @Test(expected = UnsupportedOperationException.class) public void testSocketRecoveryInterval() { final Socket socket = ctx.socket(SocketType.REP); assertThat(socket, notNullValue()); try { socket.setRecoveryInterval(42L); } finally { socket.close(); } } @Test public void testSocketgetRecoveryInterval() { final Socket socket = ctx.socket(SocketType.REP); assertThat(socket, notNullValue()); long reconnect = socket.getRecoveryInterval(); assertThat(reconnect, is(10000L)); socket.close(); } @Test public void testSocketReqCorrelate() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setReqCorrelate(true); assertThat(rc, is(true)); rc = socket.setReqCorrelate(false); assertThat(rc, is(true)); socket.close(); } @SuppressWarnings("deprecation") @Test(expected = UnsupportedOperationException.class) public void testSocketGetReqCorrelate() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); try { socket.getReqCorrelate(); } finally { socket.close(); } } @Test public void testSocketReqRelaxed() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setReqRelaxed(true); assertThat(rc, is(true)); rc = socket.setReqRelaxed(false); assertThat(rc, is(true)); socket.close(); } @SuppressWarnings("deprecation") @Test(expected = UnsupportedOperationException.class) public void testSocketGetReqRelaxed() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); try { socket.getReqRelaxed(); } finally { socket.close(); } } @Test public void testSocketRouterHandover() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); boolean rc = socket.setRouterHandover(true); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketRouterMandatory() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); boolean rc = socket.setRouterMandatory(true); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketRouterRaw() { final Socket socket = ctx.socket(SocketType.ROUTER); assertThat(socket, notNullValue()); boolean rc = socket.setRouterRaw(true); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketSocksProxy() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setSocksProxy("abc"); assertThat(rc, is(true)); String proxy = socket.getSocksProxy(); assertThat(proxy, is("abc")); rc = socket.setSocksProxy("def".getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); proxy = socket.getSocksProxy(); assertThat(proxy, is("def")); socket.close(); } @SuppressWarnings("deprecation") @Test(expected = UnsupportedOperationException.class) public void testSocketSwap() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); try { socket.setSwap(42L); } finally { socket.close(); } } @SuppressWarnings("deprecation") @Test public void testSocketGetSwap() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); long rc = socket.getSwap(); assertThat(rc, is(-1L)); socket.close(); } @SuppressWarnings("deprecation") @Test public void testSocketTCPKeepAlive() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setTCPKeepAlive(1L); assertThat(rc, is(true)); int tcp = socket.getTCPKeepAlive(); assertThat(tcp, is(1)); long tcpl = socket.getTCPKeepAliveSetting(); assertThat(tcpl, is(1L)); socket.close(); } @Test public void testSocketTCPKeepAliveCount() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setTCPKeepAliveCount(42); assertThat(rc, is(true)); long tcp = socket.getTCPKeepAliveCount(); assertThat(tcp, is(42L)); socket.close(); } @Test public void testSocketTCPKeepAliveInterval() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setTCPKeepAliveInterval(42); assertThat(rc, is(true)); long tcp = socket.getTCPKeepAliveInterval(); assertThat(tcp, is(42L)); socket.close(); } @Test public void testSocketTCPKeepAliveIdle() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setTCPKeepAliveIdle(42); assertThat(rc, is(true)); long tcp = socket.getTCPKeepAliveIdle(); assertThat(tcp, is(42L)); socket.close(); } @Test public void testSocketTos() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); boolean rc = socket.setTos(42); assertThat(rc, is(true)); int tos = socket.getTos(); assertThat(tos, is(42)); socket.close(); } @Test public void testSocketType() { final Socket socket = ctx.socket(SocketType.REQ); assertThat(socket, notNullValue()); SocketType rc = socket.getSocketType(); assertThat(rc, is(SocketType.REQ)); socket.close(); } @Test public void testSocketXpubNoDrop() { final Socket socket = ctx.socket(SocketType.XPUB); assertThat(socket, notNullValue()); boolean rc = socket.setXpubNoDrop(true); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketXpubVerbose() { final Socket socket = ctx.socket(SocketType.XPUB); assertThat(socket, notNullValue()); boolean rc = socket.setXpubVerbose(true); assertThat(rc, is(true)); socket.close(); } @Test public void testSocketZAPDomain() { final Socket socket = ctx.socket(SocketType.XPUB); assertThat(socket, notNullValue()); boolean rc = socket.setZAPDomain("abc"); assertThat(rc, is(true)); String domain = socket.getZAPDomain(); assertThat(domain, is("abc")); domain = socket.getZapDomain(); assertThat(domain, is("abc")); rc = socket.setZapDomain("def"); assertThat(rc, is(true)); domain = socket.getZapDomain(); assertThat(domain, is("def")); domain = socket.getZAPDomain(); assertThat(domain, is("def")); rc = socket.setZapDomain("ghi".getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); domain = socket.getZapDomain(); assertThat(domain, is("ghi")); domain = socket.getZAPDomain(); assertThat(domain, is("ghi")); rc = socket.setZAPDomain("jkl".getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); domain = socket.getZapDomain(); assertThat(domain, is("jkl")); domain = socket.getZAPDomain(); assertThat(domain, is("jkl")); socket.close(); } @Test public void testSocketLocalAddressPropertyName() { try (final Socket socket = ctx.socket(SocketType.XPUB)) { assertThat(socket, notNullValue()); boolean rc = socket.setSelfAddressPropertyName("X-LocalAddress"); assertThat(rc, is(true)); String propertyName = socket.getSelfAddressPropertyName(); assertThat(propertyName, is("X-LocalAddress")); } } } jeromq-0.6.0/src/test/java/org/zeromq/TestZPoller.java000066400000000000000000000375351455771126300227270ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Pipe; import java.nio.channels.SelectableChannel; import java.nio.channels.Selector; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.zeromq.ZMQ.Socket; import org.zeromq.ZPoller.EventsHandler; import org.zeromq.ZPoller.ItemCreator; import org.zeromq.ZPoller.ItemHolder; import zmq.ZError; import zmq.util.function.BiFunction; public class TestZPoller { private static final class EventsHandlerCounter implements BiFunction { private final AtomicInteger count; private EventsHandlerCounter(AtomicInteger count) { this.count = count; } @Override public Boolean apply(SelectableChannel channel, Integer events) { assertThat((events & ZPoller.IN) > 0, is(true)); try { Pipe.SourceChannel sc = (Pipe.SourceChannel) channel; sc.read(ByteBuffer.allocate(5)); count.incrementAndGet(); return true; } catch (IOException e) { return false; } } } private static final class EventsHandlerErrorCounter implements BiFunction { private final AtomicInteger error; private EventsHandlerErrorCounter(AtomicInteger error) { this.error = error; } @Override public Boolean apply(SelectableChannel channel, Integer events) { error.incrementAndGet(); return false; } } private static class EventsHandlerAdapter implements EventsHandler { @Override public boolean events(SelectableChannel channel, int events) { return false; } @Override public boolean events(Socket socket, int events) { return false; } } static class Server extends Thread { private final int port; private final Socket socket; private final ZPoller poller; public Server(ZContext context, int port) { this.port = port; socket = context.createSocket(SocketType.PUSH); poller = new ZPoller(context); } @Override public void run() { socket.bind("tcp://127.0.0.1:" + port); poller.register(socket, ZPoller.WRITABLE); while (!Thread.currentThread().isInterrupted()) { poller.poll(-1); if (poller.isWritable(socket)) { ZMsg msg = new ZMsg(); msg.add("OK"); msg.send(socket); break; } else { fail("unable to get server socket in writable state"); } } socket.close(); poller.close(); } } @Test(timeout = 5000) public void testPollerPollout() throws IOException, InterruptedException { final int port = Utils.findOpenPort(); try (ZContext context = new ZContext(); ZPoller poller = new ZPoller(context); ZMQ.Socket receiver = context.createSocket(SocketType.PULL)) { context.setLinger(5000); final Server client = new Server(context, port); client.start(); receiver.connect("tcp://127.0.0.1:" + port); final AtomicReference msg = new AtomicReference<>(); poller.register(receiver, new EventsHandlerAdapter() { @Override public boolean events(Socket s, int events) { if (receiver.equals(s)) { msg.set(ZMsg.recvMsg(receiver)); return false; } else { return true; } } }, ZPoller.IN); int maxAttempts = 5; while (!Thread.currentThread().isInterrupted() && maxAttempts-- > 0) { int rc = poller.poll(1000); if (rc < 0) { break; } } client.join(); assertThat("unable to receive msg after several cycles", msg, notNullValue()); } } @Test(timeout = 5000) public void testUseNull() { final ZContext context = new ZContext(); ZPoller poller = new ZPoller(context); SelectableChannel channel = null; Socket socket = null; // ctx.createSocket(ZMQ.SUB); boolean rc; rc = poller.register(socket, ZPoller.IN); assertThat("Registering a null socket was successful", rc, is(false)); rc = poller.register(channel, ZPoller.OUT); assertThat("Registering a null channel was successful", rc, is(false)); int events = poller.poll(10); assertThat("reading event on without sockets", events, is(0)); rc = poller.isReadable(socket); assertThat("checking read event on a null socket was successful", rc, is(false)); rc = poller.writable(socket); assertThat("checking write event on a null socket was successful", rc, is(false)); rc = poller.readable(channel); assertThat("checking read event on a null channel was successful", rc, is(false)); rc = poller.isWritable(channel); assertThat("checking write event on a null channel was successful", rc, is(false)); EventsHandler global = null; poller.setGlobalHandler(global); EventsHandler handler = null; rc = poller.register(socket, handler, ZPoller.ERR); assertThat("Register with handler on a null socket was successful", rc, is(false)); rc = poller.register(channel, ZPoller.ERR); assertThat("Register with handler on a null channel was successful", rc, is(false)); events = poller.poll(10); assertThat("reading event with events handlers without sockets", events, is(0)); poller.close(); context.close(); } @Test(timeout = 5000) public void testZPollerNew() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { ZPoller other = new ZPoller(poller); other.close(); ItemCreator itemCreator = new ZPoller.SimpleCreator(); other = new ZPoller(itemCreator, poller); other.close(); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testGlobalHandler() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { assertThat(poller.getGlobalHandler(), nullValue()); EventsHandler handler = new EventsHandlerAdapter(); poller.setGlobalHandler(handler); assertThat(poller.getGlobalHandler(), is(handler)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testItemEqualsBasic() { ZContext ctx = new ZContext(); ItemCreator itemCreator = new ZPoller.SimpleCreator(); ZPoller poller = new ZPoller(itemCreator, ctx); try { Socket socket = ctx.createSocket(SocketType.ROUTER); ItemHolder holder = itemCreator.create(socket, null, 0); assertThat(holder, is(holder)); assertThat(holder, is(not(equalTo(null)))); assertThat(holder.equals(""), is(false)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testItemEquals() { ZContext ctx = new ZContext(); ItemCreator itemCreator = new ZPoller.SimpleCreator(); ZPoller poller = new ZPoller(itemCreator, ctx); try { Socket socket = ctx.createSocket(SocketType.ROUTER); ItemHolder holder = poller.create(socket, null, 0); ItemHolder other = new ZPoller.ZPollItem(socket, null, 0); assertThat(other, is(equalTo(holder))); other = new ZPoller.ZPollItem(socket, new EventsHandlerAdapter(), 0); assertThat(other, is(not(equalTo(holder)))); socket = ctx.createSocket(SocketType.ROUTER); other = new ZPoller.ZPollItem(socket, null, 0); assertThat(other, is(not(equalTo(holder)))); other = itemCreator.create((SelectableChannel) null, null, 0); assertThat(other, is(not(equalTo(holder)))); holder = poller.create(socket, new EventsHandlerAdapter(), 0); other = new ZPoller.ZPollItem(socket, new EventsHandlerAdapter(), 0); assertThat(other, is(not(equalTo(holder)))); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testReadable() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { Socket socket = ctx.createSocket(SocketType.XPUB); poller.register(socket, new EventsHandlerAdapter()); boolean rc = poller.readable(socket); assertThat(rc, is(false)); rc = poller.isReadable(socket); assertThat(rc, is(false)); rc = poller.pollin(socket); assertThat(rc, is(false)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testWritable() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { Socket socket = ctx.createSocket(SocketType.XPUB); poller.register(socket, ZPoller.OUT); boolean rc = poller.writable(socket); assertThat(rc, is(false)); rc = poller.isWritable(socket); assertThat(rc, is(false)); rc = poller.pollout(socket); assertThat(rc, is(false)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testError() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { Socket socket = ctx.createSocket(SocketType.XPUB); poller.register(socket, ZPoller.ERR); boolean rc = poller.error(socket); assertThat(rc, is(false)); rc = poller.isError(socket); assertThat(rc, is(false)); rc = poller.pollerr(socket); assertThat(rc, is(false)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testRegister() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { Socket socket = ctx.createSocket(SocketType.XPUB); ItemHolder holder = poller.create(socket, null, 0); boolean rc = poller.register(holder); assertThat(rc, is(true)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testItems() { ZContext ctx = new ZContext(); ZPoller poller = new ZPoller(ctx); try { Socket socket = ctx.createSocket(SocketType.XPUB); Iterable items = poller.items(null); assertThat(items, notNullValue()); ItemHolder holder = poller.create(socket, null, 0); poller.register(holder); items = poller.items(socket); assertThat(items, hasItem(holder)); } finally { poller.close(); ctx.close(); } } @Test(timeout = 5000) public void testMultipleRegistrations() throws IOException { Selector selector = Selector.open(); AtomicInteger count = new AtomicInteger(); AtomicInteger error = new AtomicInteger(); BiFunction cb1 = new EventsHandlerCounter(count); BiFunction cb2 = new EventsHandlerCounter(count); BiFunction cberr = new EventsHandlerErrorCounter(error); Pipe[] pipes = new Pipe[2]; try ( ZPoller poller = new ZPoller(selector)) { pipes[0] = pipe(poller, cberr, cb1, cb2); pipes[1] = pipe(poller, cberr, cb1, cb2); int max = 10; do { poller.poll(100); --max; } while (count.get() != 4 && max > 0); assertThat("Not all events handlers checked", error.get(), is(equalTo(0))); if (max == 0 && count.get() != 4) { fail("Unable to poll after 10 attempts"); } } finally { selector.close(); for (Pipe pipe : pipes) { pipe.sink().close(); pipe.source().close(); } } } @SafeVarargs private final Pipe pipe(ZPoller poller, BiFunction errors, BiFunction... ins) throws IOException { Pipe pipe = Pipe.open(); Pipe.SourceChannel source = pipe.source(); source.configureBlocking(false); for (BiFunction handler : ins) { poller.register(source, handler, ZPoller.IN); } poller.register(source, errors, ZPoller.ERR); pipe.sink().write(ByteBuffer.wrap(new byte[] { 1, 2, 3 })); return pipe; } @Test(timeout = 5000) public void testIssue729() throws IOException { int port = Utils.findOpenPort(); ZContext ctx = new ZContext(); Socket sub = ctx.createSocket(SocketType.SUB); Socket pub = ctx.createSocket(SocketType.PUB); sub.bind("tcp://127.0.0.1:" + port); pub.connect("tcp://127.0.0.1:" + port); boolean subscribe = sub.subscribe(""); assertTrue("SUB Socket could not subscribe", subscribe); ZPoller zPoller = new ZPoller(ctx); try { zPoller.register(new ZPoller.ZPollItem(sub, null, ZPoller.POLLIN)); Thread server = new Thread(() -> { while (true) { try { pub.send("hello"); Thread.sleep(100); } catch (InterruptedException ignored) { } catch (ZMQException exc) { assertThat(exc.getErrorCode(), is(ZError.ETERM)); } } }); server.start(); int rc = zPoller.poll(-1); server.interrupt(); assertThat("ZPoller does not understand SUB socket signaled", rc, is(1)); boolean pollin = zPoller.pollin(sub); assertTrue(pollin); String hello = sub.recvStr(); assertThat("recieved message are not identical to what has been sent", hello, is("hello")); } finally { zPoller.close(); ctx.close(); } } } jeromq-0.6.0/src/test/java/org/zeromq/TestZProxy.java000066400000000000000000000345571455771126300226140ustar00rootroot00000000000000package org.zeromq; import java.io.IOException; import java.util.Collections; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class TestZProxy { private final class ProxyProvider extends ZProxy.Proxy.SimpleProxy { private int frontPort; private int backPort; private int capturePort; public ProxyProvider() { } @Override public Socket create(ZContext ctx, ZProxy.Plug place, Object... extraArgs) { Socket socket = null; if (place == ZProxy.Plug.FRONT) { socket = ctx.createSocket(SocketType.ROUTER); } if (place == ZProxy.Plug.BACK) { socket = ctx.createSocket(SocketType.DEALER); } return socket; } @Override public boolean configure(Socket socket, ZProxy.Plug place, Object... extrArgs) { if (place == ZProxy.Plug.FRONT) { frontPort = socket.bindToRandomPort("tcp://127.0.0.1"); } if (place == ZProxy.Plug.BACK) { backPort = socket.bindToRandomPort("tcp://127.0.0.1"); } if (place == ZProxy.Plug.CAPTURE && socket != null) { capturePort = socket.bindToRandomPort("tcp://127.0.0.1"); } return true; } @Override public boolean restart(ZMsg cfg, Socket socket, ZProxy.Plug place, Object... extraArgs) { // System.out.println("HOT restart msg : " + cfg); if (place == ZProxy.Plug.FRONT) { socket.unbind("tcp://127.0.0.1:" + frontPort); waitSomeTime(); frontPort = socket.bindToRandomPort("tcp://127.0.0.1"); } if (place == ZProxy.Plug.BACK) { socket.unbind("tcp://127.0.0.1:" + backPort); waitSomeTime(); backPort = socket.bindToRandomPort("tcp://127.0.0.1"); } if (place == ZProxy.Plug.CAPTURE && socket != null) { socket.unbind("tcp://127.0.0.1:" + capturePort); waitSomeTime(); capturePort = socket.bindToRandomPort("tcp://127.0.0.1"); } String msg = cfg.popString(); return "COLD".equals(msg); } @Override public boolean configure(Socket pipe, ZMsg cfg, Socket frontend, Socket backend, Socket capture, Object... args) { assert (cfg.popString().equals("TEST-CONFIG")); ZMsg msg = new ZMsg(); msg.add("TODO"); msg.send(pipe); return true; } @Override public boolean custom(Socket pipe, String cmd, Socket frontend, Socket backend, Socket capture, Object... args) { // TODO test custom commands return super.custom(pipe, cmd, frontend, backend, capture, args); } } // @Test public void testRepeated() throws IOException { for (int idx = 0; idx < 2500; ++idx) { System.out.println("+++++++++++ " + idx); testAllOptionsAsync(); testAllOptionsAsyncNew(); testAllOptionsSync(); testAllOptionsSyncNew(); testAllOptionsSyncNewCold(); testAllOptionsSyncNewHot(); } } @Test public void testAllOptionsAsync() { System.out.println("All options async"); ZContext ctx = nullContext(); testAllOptionsAsync(ctx, null); wait4NullContext(ctx); } @Test public void testAllOptionsAsyncNew() { System.out.println("All options async new"); ZContext ctx = newContext(); testAllOptionsAsync(ctx, null); wait4NewContext(ctx); } @Test public void testAllOptionsSync() { System.out.println("All options sync"); ZContext ctx = nullContext(); testAllOptionsSync(ctx, null); wait4NullContext(ctx); } @Test public void testAllOptionsSyncNew() { System.out.println("All options sync new"); ZContext ctx = newContext(); testAllOptionsSync(ctx, null); wait4NewContext(ctx); } @Test public void testAllOptionsSyncNewHot() { System.out.println("All options sync new hot"); ZContext ctx = newContext(); ZMsg hot = new ZMsg(); hot.add("HOT"); testAllOptionsSync(ctx, hot); wait4NewContext(ctx); } @Test public void testAllOptionsSyncNewCold() { System.out.println("All options sync new hot with restart"); ZContext ctx = newContext(); ZMsg hot = new ZMsg(); hot.add("COLD"); testAllOptionsSync(ctx, hot); wait4NewContext(ctx); } @Test public void testStateSync() { System.out.println("State sync"); ZContext ctx = newContext(); testStateSync(ctx); wait4NewContext(ctx); } @Test public void testStateSyncPause() { System.out.println("State sync pause"); ZContext ctx = newContext(); testStateSyncPause(ctx); wait4NewContext(ctx); } @Test public void testStateASync() { System.out.println("State async"); ZContext ctx = newContext(); testStateASync(ctx); wait4NewContext(ctx); } @After public void testSignalsSelectors() { wait4NullContext(null); } private ZContext nullContext() { return null; } private void wait4NullContext(ZContext ctx) { // it's necessary to wait a bit for unregistering selectors and signalers LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(300, TimeUnit.MILLISECONDS)); } private void waitSomeTime() { // it's necessary to wait a bit for unregistering selectors and signalers LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(10, TimeUnit.MILLISECONDS)); } private ZContext newContext() { return new ZContext(); } private void wait4NewContext(ZContext ctx) { ctx.close(); } private void testAllOptionsAsync(ZContext ctx, ZMsg hot) { ZProxy proxy = ZProxy.newProxy( ctx, "ProxyAsync" + (ctx == null ? "Null" : ""), new ProxyProvider(), "ABRACADABRA", Collections.singletonList("TEST")); final boolean async = false; String status; status = proxy.status(async); Assert.assertEquals("async status before any operation is not good!", ZProxy.ALIVE, status); status = proxy.start(async); Assert.assertEquals("Start async status is not good!", ZProxy.STOPPED, status); status = proxy.restart(hot); Assert.assertEquals("Restart async status is not good!", ZProxy.STARTED, status); status = proxy.pause(async); Assert.assertEquals("Pause async status is not good!", ZProxy.STARTED, status); status = proxy.restart(hot); Assert.assertEquals("Restart async status is not good!", ZProxy.PAUSED, status); status = proxy.stop(async); Assert.assertEquals("Stop async status is not good!", ZProxy.PAUSED, status); status = proxy.restart(hot); Assert.assertEquals("Restart async status is not good!", ZProxy.STOPPED, status); status = proxy.status(async); Assert.assertEquals("async status is not good!", ZProxy.STOPPED, status); ZMsg msg = new ZMsg(); msg.add("TEST-CONFIG"); ZMsg recvd = proxy.configure(msg); Assert.assertEquals("TODO", recvd.popString()); status = proxy.exit(); Assert.assertTrue("exit status is not good!", ZProxy.EXITED.equals(status)); status = proxy.status(); Assert.assertTrue("exit status is not good!", ZProxy.EXITED.equals(status)); } private void testAllOptionsSync(ZContext ctx, ZMsg hot) { ZProxy proxy = ZProxy.newProxy( ctx, "ProxySync" + (ctx == null ? "Null" : ""), new ProxyProvider(), "ABRACADABRA", Collections.singletonList("TEST")); final boolean sync = true; String status; status = proxy.status(sync); Assert.assertEquals("sync status before any operation is not good!", ZProxy.STOPPED, status); status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STARTED, status); status = proxy.restart(hot == null ? null : hot.duplicate()); Assert.assertEquals("Restart sync status is not good!", ZProxy.STARTED, status); status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.restart(hot == null ? null : hot.duplicate()); Assert.assertEquals("Restart sync status is not good!", ZProxy.PAUSED, status); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STOPPED, status); status = proxy.restart(hot == null ? null : hot.duplicate()); Assert.assertEquals("Restart sync status is not good!", ZProxy.STOPPED, status); status = proxy.status(sync); Assert.assertEquals("sync status is not good!", ZProxy.STOPPED, status); ZMsg msg = new ZMsg(); msg.add("TEST-CONFIG"); ZMsg recvd = proxy.configure(msg); Assert.assertEquals("TODO", recvd.popString()); status = proxy.status(sync); Assert.assertEquals("sync status is not good!", ZProxy.STOPPED, status); status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.exit(); Assert.assertEquals("exit status is not good!", ZProxy.EXITED, status); } private void testStateSync(ZContext ctx) { final boolean sync = true; String status; ZProxy proxy = ZProxy.newProxy( ctx, "ProxyStateSync" + (ctx == null ? "Null" : ""), new ProxyProvider(), "ABRACADABRA", Collections.singletonList("TEST")); status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STARTED, status); status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STOPPED, status); waitSomeTime(); // start the proxy after stopping it status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STARTED, status); waitSomeTime(); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STOPPED, status); waitSomeTime(); // pause the proxy after stopping it status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.exit(); Assert.assertEquals("exit status is not good!", ZProxy.EXITED, status); } private void testStateSyncPause(ZContext ctx) { final boolean sync = true; String status; ZProxy proxy = ZProxy.newProxy( ctx, "ProxyStatePauseSync" + (ctx == null ? "Null" : ""), new ProxyProvider(), "ABRACADABRA", Collections.singletonList("TEST")); status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STARTED, status); waitSomeTime(); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STOPPED, status); waitSomeTime(); // start the proxy after stopping it status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STARTED, status); waitSomeTime(); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STOPPED, status); waitSomeTime(); // pause the proxy after stopping it status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.PAUSED, status); status = proxy.exit(); Assert.assertEquals("exit status is not good!", ZProxy.EXITED, status); } private void testStateASync(ZContext ctx) { final boolean sync = false; String status; ZProxy proxy = ZProxy.newProxy( ctx, "ProxyStateASync" + (ctx == null ? "Null" : ""), new ProxyProvider(), "ABRACADABRA", Collections.singletonList("TEST")); status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.ALIVE, status); status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.STARTED, status); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.PAUSED, status); waitSomeTime(); // start the proxy after stopping it status = proxy.start(sync); Assert.assertEquals("Start sync status is not good!", ZProxy.STOPPED, status); waitSomeTime(); status = proxy.stop(sync); Assert.assertEquals("Stop sync status is not good!", ZProxy.STARTED, status); waitSomeTime(); // pause the proxy after stopping it status = proxy.pause(sync); Assert.assertEquals("Pause sync status is not good!", ZProxy.STOPPED, status); status = proxy.exit(); Assert.assertEquals("exit status is not good!", ZProxy.EXITED, status); } } jeromq-0.6.0/src/test/java/org/zeromq/TestZStar.java000066400000000000000000000054541455771126300223760ustar00rootroot00000000000000package org.zeromq; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Socket; import org.zeromq.ZStar.Fortune; import org.zeromq.ZStar.Star; public class TestZStar { private static final class BlackHole implements ZStar.Fortune { @Override public String premiere(Socket mic, Object... args) { return "Test " + Arrays.toString(args); } @Override public Star create(ZContext ctx, Socket pipe, int count, Star previous, Object... args) { return new NoNo(); } @Override public boolean interview(Socket mic) { System.out.print("Fortune is not here anymore ..."); // tell that star is not here anymore return true; } @Override public void party(ZContext ctx) { // do nothing System.out.print(" Cleaning the remains.. "); } } private static final class NoNo implements ZStar.Star { @Override public void prepare() { // do nothing } @Override public int breathe() { return -1; } @Override public boolean act(int events) { return false; } @Override public boolean entract() { return false; } @Override public boolean renews() { return false; } } @Test public void testNoStar() { System.out.print("No star: "); ZStar.Fortune fortune = new BlackHole(); ZStar.Entourage entourage = new ZStar.Entourage() { @Override public void breakaleg(ZContext ctx, Fortune fortune, Socket phone, Object... bags) { // Crepi il lupo! } @Override public void party(ZContext ctx) { // right now there are some random closing issues ZStar.party(30, TimeUnit.MILLISECONDS); // waited a bit here seems to arrange that. // no user penalty cost, the show is over. } }; ZStar star = new ZStar(fortune, "motdelafin", Arrays.asList("TEST", entourage).toArray()); ZMsg msg = star.recv(); Assert.assertNull("Able to receive a message from a black hole", msg); boolean rc = star.sign(); Assert.assertFalse("Able to detect the presence of a black hole", rc); rc = star.send("whatever"); Assert.assertFalse("Able to send a command to a black hole", rc); // don't try it // rc = star.pipe().send("boom ?!"); System.out.println("."); } } jeromq-0.6.0/src/test/java/org/zeromq/TestZThread.java000066400000000000000000000053101455771126300226630ustar00rootroot00000000000000package org.zeromq; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQ.Socket; import zmq.ZError; import java.util.concurrent.CountDownLatch; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestZThread { @Test public void testDetached() { final CountDownLatch stopped = new CountDownLatch(1); ZThread.start((args) -> { try (ZContext ctx = new ZContext()) { Socket push = ctx.createSocket(SocketType.PUSH); assertThat(push, notNullValue()); } stopped.countDown(); }); try { stopped.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } @Test public void testFork() { final ZContext ctx = new ZContext(); Socket pipe = ZThread.fork(ctx, (args, ctx1, pipe1) -> { // Create a socket to check it'll be automatically deleted ctx1.createSocket(SocketType.PUSH); pipe1.recvStr(); pipe1.send("pong"); }); assertThat(pipe, notNullValue()); pipe.send("ping"); String pong = pipe.recvStr(); assertThat(pong, is("pong")); // Everything should be cleanly closed now ctx.close(); } @Test(timeout = 1000) public void testCriticalException() throws InterruptedException { final CountDownLatch stopped = new CountDownLatch(1); try (final ZContext ctx = new ZContext()) { ctx.setUncaughtExceptionHandler((t, e) -> stopped.countDown()); Socket pipe = ZThread.fork(ctx, (args, ctx1, pipe1) -> { throw new Error("critical"); }); assertThat(pipe, notNullValue()); stopped.await(); } } @Test @Ignore public void testClosePipe() { ZContext ctx = new ZContext(); Socket pipe = ZThread.fork(ctx, (args, ctx1, pipe1) -> { pipe1.recvStr(); pipe1.send("pong"); }); assertThat(pipe, notNullValue()); boolean rc = pipe.send("ping"); assertThat(rc, is(true)); String pong = pipe.recvStr(); assertThat(pong, is("pong")); try { rc = pipe.send("boom ?!"); assertThat("pipe was closed pretty fast", rc, is(true)); } catch (ZMQException e) { int errno = e.getErrorCode(); assertThat("Expected exception has the wrong code", ZError.ETERM, is(errno)); } ctx.close(); } } jeromq-0.6.0/src/test/java/org/zeromq/TooManyOpenFilesTester.java000066400000000000000000000126421455771126300250520ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; /** * Tests exhaustion of java file pipes, * each component being on a separate thread. * */ @Ignore public class TooManyOpenFilesTester { private static final long REQUEST_TIMEOUT = 2000; // msecs /** * A simple server for one reply only. * */ private class Server extends Thread { private final int port; /** * Creates a new server. * @param port the port to which to connect. * @param idx the index of the server */ public Server(int port, int idx) { this.port = port; setName("Server-" + idx); } @Override public void run() { ZContext ctx = new ZContext(1); Socket server = ctx.createSocket(SocketType.ROUTER); server.bind("tcp://localhost:" + port); byte[] msg = server.recv(0); byte[] address = msg; poll(ctx, server); msg = server.recv(0); byte[] delimiter = msg; poll(ctx, server); msg = server.recv(0); // only one echo message for this server server.send(address, ZMQ.SNDMORE); server.send(delimiter, ZMQ.SNDMORE); server.send(msg, 0); // Clean up. ctx.destroy(); } } /** * Simple client. * */ private class Client extends Thread { private final int port; final AtomicBoolean finished = new AtomicBoolean(); /** * Creates a new client. * @param port the port to which to connect. */ public Client(int port, int idx) { this.port = port; setName("Client-" + idx); } @Override public void run() { ZContext ctx = new ZContext(1); Socket client = ctx.createSocket(SocketType.REQ); client.setIdentity("ID".getBytes()); client.connect("tcp://localhost:" + port); client.send("DATA", 0); inBetween(ctx, client); byte[] reply = client.recv(0); assertThat(reply, notNullValue()); assertThat(new String(reply, ZMQ.CHARSET), is("DATA")); // Clean up. ctx.destroy(); finished.set(true); } /** * Called between the request-reply cycle. * @param client the socket participating to the cycle of request-reply * @param selector the selector used for polling */ protected void inBetween(ZContext ctx, Socket client) { poll(ctx, client); } } /** * Polls while keeping the selector opened. * @param socket the socket to poll * @param selector the selector used for polling */ private void poll(ZContext ctx, Socket socket) { // Poll socket for a reply, with timeout Poller poller = ctx.createPoller(1); poller.register(socket, Poller.POLLIN); int rc = poller.poll(REQUEST_TIMEOUT); assertThat(rc, is(1)); boolean readable = poller.pollin(0); assertThat(readable, is(true)); poller.close(); } /** * Test exhaustion of java pipes. * Exhaustion can currently come from zmq.Signaler that are not closed * or from {@link java.nio.channels.Selector} that are not closed. * @throws Exception if something bad occurs. */ @Test public void testReqRouterTcpPoll() throws Exception { // we have no direct way to test this, except by running a bunch of tests and waiting for the failure to happen... // crashed on iteration 3000-ish in my machine for poll selectors; on iteration 16-ish for sockets for (int index = 0; index < 10000; ++index) { long start = System.currentTimeMillis(); List pairs = new ArrayList<>(); for (int idx = 0; idx < 20; ++idx) { Pair pair = testWithPoll(idx); pairs.add(pair); } for (Pair p : pairs) { p.server.join(); p.client.join(); } boolean finished = true; for (Pair p : pairs) { finished &= p.client.finished.get(); } long end = System.currentTimeMillis(); assertThat(finished, is(true)); System.out.printf("Test %s finished in %s millis.\n", index, (end - start)); } } /** * Dummy class to help keep relation between client and server. */ private class Pair { private Client client; private Server server; } private Pair testWithPoll(int idx) throws IOException { int port = Utils.findOpenPort(); Server server = new Server(port, idx); server.start(); Client client = new Client(port, idx); client.start(); Pair pair = new Pair(); pair.server = server; pair.client = client; return pair; } } jeromq-0.6.0/src/test/java/org/zeromq/XpubXsubZTest.java000066400000000000000000000171341455771126300232430ustar00rootroot00000000000000package org.zeromq; import org.junit.Test; import org.zeromq.ZMQ.Socket; import org.zeromq.ZProxy.Plug; import zmq.util.Utils; import java.io.IOException; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; public class XpubXsubZTest { private AtomicReference testIssue476(final int front, final int back, final int max, ExecutorService service, final ZContext ctx) throws InterruptedException, ExecutionException { final AtomicInteger numberReceived = new AtomicInteger(0); Future subscriber = service.submit(() -> { Thread.currentThread().setName("Subscriber"); try (ZContext ctx1 = new ZContext()) { final Socket requester = ctx1.createSocket(SocketType.SUB); requester.connect("tcp://localhost:" + back); requester.subscribe("hello".getBytes(ZMQ.CHARSET)); while (numberReceived.get() < max) { ZMsg.recvMsg(requester); numberReceived.incrementAndGet(); } } }); final AtomicReference error = new AtomicReference<>(); service.submit(() -> { Thread.currentThread().setName("Publisher"); try { Socket pub = ctx.createSocket(SocketType.PUB); pub.connect("tcp://localhost:" + front); while (numberReceived.get() < max) { ZMsg message = ZMsg.newStringMsg("hello", "world"); boolean rc = message.send(pub); assertThat(rc, is(true)); ZMQ.msleep(5); } } catch (Throwable ex) { error.set(ex); ex.printStackTrace(); } }); try { subscriber.get(5, TimeUnit.SECONDS); } catch (TimeoutException e) { System.err.println("Timeout waiting for subscriber to get " + max + " messages."); error.set(e); e.printStackTrace(); numberReceived.set(max + 1); // Make sure the threads will finish } ZMQ.msleep(300); return error; } @Test public void testIssue476() throws InterruptedException, IOException, ExecutionException { final int front = Utils.findOpenPort(); final int back = Utils.findOpenPort(); final int max = 20; ExecutorService service = Executors.newFixedThreadPool(3); try (final ZContext ctx = new ZContext()) { service.submit(() -> { Thread.currentThread().setName("Proxy"); Socket xpub = ctx.createSocket(SocketType.XPUB); xpub.bind("tcp://*:" + back); Socket xsub = ctx.createSocket(SocketType.XSUB); xsub.bind("tcp://*:" + front); Socket ctrl = ctx.createSocket(SocketType.PAIR); ctrl.bind("inproc://ctrl-proxy"); ZMQ.proxy(xpub, xsub, null, ctrl); }); final AtomicReference error = testIssue476(front, back, max, service, ctx); ZMQ.Socket ctrl = ctx.createSocket(SocketType.PAIR); ctrl.connect("inproc://ctrl-proxy"); ctrl.send(ZMQ.PROXY_TERMINATE); ctrl.close(); service.shutdown(); service.awaitTermination(2, TimeUnit.SECONDS); assertThat(error.get(), nullValue()); } } @SuppressWarnings("deprecation") @Test public void testIssue476WithZProxy() throws InterruptedException, IOException, ExecutionException { final int front = Utils.findOpenPort(); final int back = Utils.findOpenPort(); final int max = 10; ExecutorService service = Executors.newFixedThreadPool(3); final ZContext ctx = new ZContext(); ZProxy.Proxy actor = new ZProxy.Proxy.SimpleProxy() { @Override public Socket create(ZContext ctx, Plug place, Object... args) { if (place == Plug.FRONT) { return ctx.createSocket(SocketType.XSUB); } if (place == Plug.BACK) { return ctx.createSocket(SocketType.XPUB); } if (place == Plug.CAPTURE) { return ctx.createSocket(SocketType.PUB); } return null; } @Override public boolean configure(Socket socket, Plug place, Object... args) { if (place == Plug.FRONT) { return socket.bind("tcp://*:" + front); } if (place == Plug.BACK) { return socket.bind("tcp://*:" + back); } return true; } }; ZProxy proxy = ZProxy.newZProxy(ctx, null, actor, UUID.randomUUID().toString()); proxy.start(true); final AtomicReference error = testIssue476(front, back, max, service, ctx); proxy.exit(false); service.shutdown(); service.awaitTermination(2, TimeUnit.SECONDS); assertThat(error.get(), nullValue()); ctx.close(); } @SuppressWarnings("deprecation") @Test public void testIssue476WithZProxyZmqPump() throws InterruptedException, IOException, ExecutionException { final int front = Utils.findOpenPort(); final int back = Utils.findOpenPort(); final int max = 10; ExecutorService service = Executors.newFixedThreadPool(3); try (final ZContext ctx = new ZContext()) { ZProxy.Proxy actor = new ZProxy.Proxy.SimpleProxy() { @Override public Socket create(ZContext ctx, Plug place, Object... args) { if (place == Plug.FRONT) { return ctx.createSocket(SocketType.XSUB); } if (place == Plug.BACK) { return ctx.createSocket(SocketType.XPUB); } if (place == Plug.CAPTURE) { return ctx.createSocket(SocketType.PUB); } return null; } @Override public boolean configure(Socket socket, Plug place, Object... args) { if (place == Plug.FRONT) { return socket.bind("tcp://*:" + front); } if (place == Plug.BACK) { return socket.bind("tcp://*:" + back); } return true; } }; ZProxy proxy = ZProxy.newProxy(ctx, "XPub-XSub", actor, UUID.randomUUID().toString()); proxy.start(true); final AtomicReference error = testIssue476(front, back, max, service, ctx); proxy.exit(false); service.shutdown(); service.awaitTermination(2, TimeUnit.SECONDS); assertThat(error.get(), nullValue()); } } } jeromq-0.6.0/src/test/java/org/zeromq/ZBeaconTest.java000066400000000000000000000136241455771126300226520ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.InetAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.junit.Ignore; import org.junit.Test; public class ZBeaconTest { @Test(timeout = 2000) public void testUseBuilder() throws InterruptedException, IOException { long interval = ZBeacon.DEFAULT_BROADCAST_INTERVAL / 2; AtomicLong beacon1 = new AtomicLong(0); AtomicLong beacon2 = new AtomicLong(0); CountDownLatch latch = new CountDownLatch(2); int port = Utils.findOpenPort(); byte[] beacondata = new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01, 0x12, 0x34 }; AtomicReference receivedBeacon = new AtomicReference<>(); ZBeacon.Builder builder = new ZBeacon.Builder() .beacon(beacondata) .ignoreLocalAddress(false) .broadcastInterval(interval) .client(InetAddress.getLoopbackAddress()).port(port) .server(InetAddress.getLoopbackAddress()) .prefix(new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01 }) .listener((sender, received) -> { if (beacon1.get() == 0) { beacon1.set(System.currentTimeMillis()); } else if (beacon2.get() == 0) { beacon2.set(System.currentTimeMillis()); receivedBeacon.set(received); } latch.countDown(); }); ZBeacon beacon = builder.build(); try { beacon.start(); assertThat(latch.await(interval * 3, TimeUnit.MILLISECONDS), is(Boolean.TRUE)); } finally { beacon.stop(); } //Ensure that the real interval is almost the required interval long delta = beacon2.get() - beacon1.get(); assertTrue(String.format("expected %d, got %d", interval, delta), (delta > interval - 10) && (delta < interval + 10)); assertThat(receivedBeacon.get(), is(beacondata)); } @SuppressWarnings("deprecation") @Test(timeout = 2000) public void testReceiveOwnBeacons() throws InterruptedException, IOException { final CountDownLatch latch = new CountDownLatch(1); byte[] beacon = new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01, 0x12, 0x34 }; byte[] prefix = new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01 }; int port = Utils.findOpenPort(); ZBeacon zbeacon = new ZBeacon("127.0.0.1", port, beacon, false); zbeacon.setPrefix(prefix); zbeacon.setListener((sender, beacon1) -> latch.countDown()); try { zbeacon.start(); latch.await(20, TimeUnit.SECONDS); assertThat(latch.getCount(), is(0L)); } finally { zbeacon.stop(); } } @SuppressWarnings("deprecation") @Test(timeout = 2000) @Ignore public void testIgnoreOwnBeacon() throws IOException, InterruptedException { final int port = Utils.findOpenPort(); final byte[] beacon = new byte[] { 'Z', 'R', 'E', 0x01, 0x2 }; final byte[] prefix = new byte[] { 'Z', 'R', 'E', 0x01 }; final ZBeacon zbeacon = new ZBeacon(ZBeacon.DEFAULT_BROADCAST_HOST, port, beacon, true); zbeacon.setPrefix(prefix); final AtomicLong counter = new AtomicLong(); zbeacon.setListener((sender, beacon1) -> { counter.incrementAndGet(); System.out.println(sender.toString()); try { System.out.println(InetAddress.getLocalHost().getHostAddress()); } catch (Exception e) { } System.out.println(new String(beacon1)); }); zbeacon.start(); zmq.ZMQ.sleep(1); zbeacon.stop(); assertThat(counter.get(), is(0L)); } @SuppressWarnings("deprecation") @Test(timeout = 2000) public void testReceiveOwnBeaconsBlocking() throws InterruptedException, IOException { final CountDownLatch latch = new CountDownLatch(1); byte[] beacon = new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01, 0x12, 0x34 }; byte[] prefix = new byte[] { 'H', 'Y', 'D', 'R', 'A', 0x01 }; int port = Utils.findOpenPort(); ZBeacon zbeacon = new ZBeacon("127.0.0.1", port, beacon, false, true); zbeacon.setPrefix(prefix); zbeacon.setListener((sender, beacon1) -> latch.countDown()); try { zbeacon.start(); latch.await(20, TimeUnit.SECONDS); assertThat(latch.getCount(), is(0L)); } finally { zbeacon.stop(); } } @SuppressWarnings("deprecation") @Test(timeout = 2000) @Ignore public void testIgnoreOwnBeaconBlocking() throws IOException, InterruptedException { final int port = Utils.findOpenPort(); final byte[] beacon = new byte[] { 'Z', 'R', 'E', 0x01, 0x2 }; final byte[] prefix = new byte[] { 'Z', 'R', 'E', 0x01 }; final ZBeacon zbeacon = new ZBeacon(ZBeacon.DEFAULT_BROADCAST_HOST, port, beacon, true, true); zbeacon.setPrefix(prefix); final AtomicLong counter = new AtomicLong(); zbeacon.setListener((sender, beacon1) -> { counter.incrementAndGet(); System.out.println(sender.toString()); try { System.out.println(InetAddress.getLocalHost().getHostAddress()); } catch (Exception e) { } System.out.println(new String(beacon1)); }); zbeacon.start(); zmq.ZMQ.sleep(1); zbeacon.stop(); assertThat(counter.get(), is(0L)); } } jeromq-0.6.0/src/test/java/org/zeromq/ZCertStoreTest.java000066400000000000000000000135001455771126300233660ustar00rootroot00000000000000package org.zeromq; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.zeromq.auth.TestUtils; import java.io.File; import java.io.IOException; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class ZCertStoreTest { private ZCertStore certStore; private static final String CERTSTORE_LOCATION = "target/testCurveCerts"; @Before public void init() { // first cleanup test-directory if still present TestUtils.cleanupDir(CERTSTORE_LOCATION); certStore = new ZCertStore(CERTSTORE_LOCATION, new ZCertStore.Timestamper()); File store = new File(CERTSTORE_LOCATION); // check if the certstore location was created by the certstore assertThat(store.exists(), is(true)); assertThat(store.isDirectory(), is(true)); // new certstore-directory should have no certs,yet assertThat(certStore.getCertificatesCount(), is(0)); } @Test public void testAddCertificates() throws IOException { final int beforeAmount = certStore.getCertificatesCount(); assertThat(beforeAmount, is(0)); ZCert c1 = new ZCert(); File f = c1.savePublic(CERTSTORE_LOCATION + "/c1.cert"); assertThat(f.exists(), is(true)); // check the store if something changed, and if yes reload all boolean rc = certStore.reloadIfNecessary(); assertThat(rc, is(true)); // is now one certificate more in the store? assertThat(certStore.getCertificatesCount(), is(1)); // check if we find our publickey using the Z85-Version to lookup assertThat(certStore.containsPublicKey(c1.getPublicKeyAsZ85()), is(true)); // check if we find our publickey using the binary-Version to lookup (this will internally be encoded to z85 for the lookup) assertThat(certStore.containsPublicKey(c1.getPublicKey()), is(true)); // check if we do not find some random lookup-key. Z85-Keys need to have a length of 40 bytes. assertThat(certStore.containsPublicKey("1234567890123456789012345678901234567890"), is(false)); zmq.ZMQ.msleep(1000); // check certs in sub-directories ZCert c2 = new ZCert(); f = c2.savePublic(CERTSTORE_LOCATION + "/sub/c2.cert"); assertThat(f.exists(), is(true)); assertThat(certStore.getCertificatesCount(), is(2)); } @Test public void testRemoveCertificates() throws IOException { int beforeAmount = certStore.getCertificatesCount(); assertThat(beforeAmount, is(0)); ZCert c1 = new ZCert(); File f = c1.savePublic(CERTSTORE_LOCATION + "/c1.cert"); assertThat(f.exists(), is(true)); // check the store if something changed, and if yes reload all boolean rc = certStore.reloadIfNecessary(); assertThat(rc, is(true)); // is now one certificate more in the store? assertThat(certStore.getCertificatesCount(), is(1)); File certificate = new File(CERTSTORE_LOCATION + "/c1.cert"); rc = certificate.exists(); assertThat(rc, is(true)); rc = certificate.delete(); assertThat(rc, is(true)); rc = certStore.reloadIfNecessary(); assertThat(rc, is(true)); assertThat(certStore.getCertificatesCount(), is(0)); // check if we find our publickey using the Z85-Version to lookup assertThat(certStore.containsPublicKey(c1.getPublicKeyAsZ85()), is(false)); // check if we find our publickey using the binary-Version to lookup (this will internally be encoded to z85 for the lookup) assertThat(certStore.containsPublicKey(c1.getPublicKey()), is(false)); } @Test public void testcheckForCertificateChanges() throws IOException { assertThat(certStore.getCertificatesCount(), is(0)); ZCert cert1 = new ZCert(); File f = cert1.savePublic(CERTSTORE_LOCATION + "/c1.cert"); assertThat(f.exists(), is(true)); ZCert cert2 = new ZCert(); f = cert2.saveSecret(CERTSTORE_LOCATION + "/sub/c2.cert"); assertThat(f.exists(), is(true)); assertThat(certStore.getCertificatesCount(), is(2)); assertThat(certStore.checkForChanges(), is(false)); zmq.ZMQ.msleep(1000); // rewrite certificates and see if this change gets recognized ZCert othercert1 = new ZCert(); f = othercert1.savePublic(CERTSTORE_LOCATION + "/c1.cert"); assertThat(f.exists(), is(true)); // change is recognized if a file is changed only in the main-folder assertThat(certStore.checkForChanges(), is(true)); // second call shall indicate change assertThat(certStore.checkForChanges(), is(true)); // reload the certificates assertThat(certStore.getCertificatesCount(), is(2)); assertThat(certStore.checkForChanges(), is(false)); assertThat(certStore.containsPublicKey(cert1.getPublicKeyAsZ85()), is(false)); assertThat(certStore.containsPublicKey(cert1.getPublicKey()), is(false)); assertThat(certStore.containsPublicKey(othercert1.getPublicKeyAsZ85()), is(true)); assertThat(certStore.containsPublicKey(othercert1.getPublicKey()), is(true)); zmq.ZMQ.msleep(1000); // check if changes in subfolders get recognized f = cert2.savePublic(CERTSTORE_LOCATION + "/sub/c2.cert"); assertThat(f.exists(), is(true)); assertThat(certStore.checkForChanges(), is(true)); // second call shall indicate change assertThat(certStore.checkForChanges(), is(true)); // reload the certificates assertThat(certStore.getCertificatesCount(), is(2)); assertThat(certStore.checkForChanges(), is(false)); } @After public void cleanup() { TestUtils.cleanupDir(CERTSTORE_LOCATION); } } jeromq-0.6.0/src/test/java/org/zeromq/ZCertTest.java000066400000000000000000000162261455771126300223610ustar00rootroot00000000000000package org.zeromq; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.zeromq.auth.TestUtils; import org.zeromq.util.ZMetadata; import zmq.util.AndroidProblematic; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @AndroidProblematic public class ZCertTest { private static final String CERT_LOCATION = "target/testCerts"; @Before public void init() { // first cleanup test-directory if still present TestUtils.cleanupDir(CERT_LOCATION); File store = new File(CERT_LOCATION); store.mkdirs(); } @After public void tearDown() { TestUtils.cleanupDir(CERT_LOCATION); } @Test(expected = IllegalArgumentException.class) public void testConstructorNullStringPublicKey() { new ZCert((String) null, null); } @Test(expected = IllegalArgumentException.class) public void testConstructorNullBytesPublicKey() { new ZCert((byte[]) null, null); } @Test(expected = IllegalArgumentException.class) public void testConstructorInvalidBytesPublicKey() { new ZCert(new byte[0], null); } @Test(expected = IllegalArgumentException.class) public void testConstructorInvalidStringPublicKey() { new ZCert("", null); } @Test public void testConstructorValidPublicKeyZ85() { ZMQ.Curve.KeyPair keyPair = ZMQ.Curve.generateKeyPair(); assertThat(keyPair.publicKey.length(), is(40)); ZCert cert = new ZCert(keyPair.publicKey); assertThat(cert.getPublicKeyAsZ85(), is(keyPair.publicKey)); assertThat(cert.getPublicKey(), is(ZMQ.Curve.z85Decode(keyPair.publicKey))); assertThat(cert.getSecretKeyAsZ85(), nullValue()); assertThat(cert.getSecretKey(), nullValue()); } @Test public void testConstructorValidPublicKey() { ZMQ.Curve.KeyPair keyPair = ZMQ.Curve.generateKeyPair(); byte[] bytes = ZMQ.Curve.z85Decode(keyPair.publicKey); ZCert cert = new ZCert(bytes, null); assertThat(cert.getPublicKeyAsZ85(), is(keyPair.publicKey)); assertThat(cert.getPublicKey(), is(bytes)); assertThat(cert.getSecretKeyAsZ85(), nullValue()); assertThat(cert.getSecretKey(), nullValue()); } @Test public void testConstructorValidKeysZ85() { ZMQ.Curve.KeyPair keyPair = ZMQ.Curve.generateKeyPair(); assertThat(keyPair.publicKey.length(), is(40)); ZCert cert = new ZCert(keyPair.publicKey, keyPair.secretKey); assertThat(cert.getPublicKeyAsZ85(), is(keyPair.publicKey)); assertThat(cert.getSecretKeyAsZ85(), is(keyPair.secretKey)); assertThat(cert.getPublicKey(), is(ZMQ.Curve.z85Decode(keyPair.publicKey))); assertThat(cert.getSecretKey(), is(ZMQ.Curve.z85Decode(keyPair.secretKey))); } @Test public void testConstructorValidKeys() { ZMQ.Curve.KeyPair keyPair = ZMQ.Curve.generateKeyPair(); byte[] bytes = ZMQ.Curve.z85Decode(keyPair.publicKey); byte[] secret = ZMQ.Curve.z85Decode(keyPair.secretKey); ZCert cert = new ZCert(bytes, secret); assertThat(cert.getPublicKeyAsZ85(), is(keyPair.publicKey)); assertThat(cert.getSecretKeyAsZ85(), is(keyPair.secretKey)); assertThat(cert.getPublicKey(), is(bytes)); assertThat(cert.getSecretKey(), is(secret)); } @Test public void testSetMeta() { ZCert cert = new ZCert(); cert.setMeta("version", "1"); String version = cert.getMeta("version"); assertThat(version, is("1")); cert.setMeta("version", "2"); version = cert.getMeta("version"); assertThat(version, is("2")); } @Test public void testGetMeta() { ZCert cert = new ZCert(); cert.setMeta("version", "1"); ZMetadata meta = cert.getMetadata(); String version = meta.get("version"); assertThat(version, is("1")); meta.set("version", "2"); version = cert.getMeta("version"); assertThat(version, is("2")); } @Test public void testUnsetMeta() { ZCert cert = new ZCert(); cert.setMeta("version", "1"); cert.unsetMeta("version"); assertThat(cert.getMeta("version"), nullValue()); } @Test public void testSavePublic() throws IOException { ZCert cert = new ZCert("uYax]JF%mz@r%ERApdOb3U", "!LeSNcjV%qv!apmqePOP:}MBWPCHfdY4IkqO=AW0"); cert.setMeta("version", "1"); StringWriter writer = new StringWriter(); cert.savePublic(writer); String datePattern = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-]?[0-9]{4}"; String expected = "# \\*\\* Generated on " + datePattern + " by ZCert \\*\\*\n" + "# ZeroMQ CURVE Public Certificate\n" + "# Exchange securely, or use a secure mechanism to verify the contents\n" + "# of this file after exchange. Store public certificates in your home\n" + "# directory, in the .curve subdirectory.\n\n" + "metadata\n" + " version = \"1\"\n" + "curve\n" + " public-key = \"uYax]JF%mz@r%ERApdOb3U\"\n"; String result = writer.toString(); assertThat(Pattern.compile(expected).matcher(result).matches(), is(true)); } @Test public void testSaveSecret() throws IOException { ZCert cert = new ZCert("uYax]JF%mz@r%ERApdOb3U", "!LeSNcjV%qv!apmqePOP:}MBWPCHfdY4IkqO=AW0"); cert.setMeta("version", "1"); StringWriter writer = new StringWriter(); cert.saveSecret(writer); String datePattern = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-]?[0-9]{4}"; String expected = "# \\*\\* Generated on " + datePattern + " by ZCert \\*\\*\n" + "# ZeroMQ CURVE \\*\\*Secret\\*\\* Certificate\n" + "# DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.\n\n" + "metadata\n" + " version = \"1\"\n" + "curve\n" + " secret-key = \"!LeSNcjV%qv!apmqePOP:}MBWPCHfdY4IkqO=AW0\"\n" + " public-key = \"uYax]JF%mz@r%ERApdOb3U\"\n"; String result = writer.toString(); assertThat(Pattern.compile(expected).matcher(result).matches(), is(true)); } @Test public void testSavePublicFile() throws IOException { ZCert cert = new ZCert(); cert.savePublic(CERT_LOCATION + "/test.cert"); File file = new File(CERT_LOCATION + "/test.cert"); assertThat(file.exists(), is(true)); } @Test public void testSaveSecretFile() throws IOException { ZCert cert = new ZCert(); cert.saveSecret(CERT_LOCATION + "/test_secret.cert"); File file = new File(CERT_LOCATION + "/test_secret.cert"); assertThat(file.exists(), is(true)); } } jeromq-0.6.0/src/test/java/org/zeromq/ZFrameTest.java000066400000000000000000000122371455771126300225140ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZMQ.Socket; public class ZFrameTest { @Test public void testZFrameCreation() { ZFrame f = new ZFrame("Hello".getBytes()); assertThat(f, notNullValue()); assertThat(f.hasData(), is(true)); assertThat(f.size(), is(5)); f = new ZFrame(); assertThat(f.hasData(), is(false)); assertThat(f.size(), is(0)); } @Test public void testZFrameEquals() { ZFrame f = new ZFrame("Hello".getBytes()); ZFrame clone = f.duplicate(); assertThat(clone, is(f)); } @Test public void testSending() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zframe.test"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zframe.test"); // Send five different frames, test ZFRAME_MORE for (int i = 0; i < 5; i++) { ZFrame f = new ZFrame("Hello".getBytes()); boolean rt = f.send(output, ZMQ.SNDMORE); assertThat(rt, is(true)); } // Send same frame five times ZFrame f = new ZFrame("Hello".getBytes()); for (int i = 0; i < 5; i++) { f.send(output, ZMQ.SNDMORE); } assertThat(f.size(), is(5)); ctx.close(); } @Test public void testCopyingAndDuplicating() { ZFrame f = new ZFrame("Hello"); ZFrame copy = f.duplicate(); assertThat(copy, is(f)); f.destroy(); assertThat(copy, is(not(f))); assertThat(copy.size(), is(5)); } @Test public void testReceiving() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zframe.test"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zframe.test"); // Send same frame five times ZFrame f = new ZFrame("Hello".getBytes()); for (int i = 0; i < 5; i++) { f.send(output, ZMQ.SNDMORE); } // Send END frame f = new ZFrame("NOT".getBytes()); f.reset("END".getBytes()); assertThat(f.strhex(), is("454E44")); f.send(output, 0); // Read and count until we receive END int frameNbr = 0; while (true) { f = ZFrame.recvFrame(input); frameNbr++; if (f.streq("END")) { f.destroy(); break; } } assertThat(frameNbr, is(6)); f = ZFrame.recvFrame(input, ZMQ.DONTWAIT); assertThat(f, nullValue()); ctx.close(); } @Test public void testStringFrames() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zframe.test"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zframe.test"); ZFrame f1 = new ZFrame("Hello"); assertThat(f1.getData().length, is(5)); f1.send(output, 0); ZFrame f2 = ZFrame.recvFrame(input); assertThat(f2.hasData(), is(true)); assertThat(f2.getData().length, is(5)); assertThat(f2.streq("Hello"), is(true)); assertThat(f2.toString(), is("Hello")); assertThat(f2, is(f1)); ctx.close(); } @Test public void testZMQClientServer() { try (ZContext ctx = new ZContext(); Socket client = ctx.createSocket(SocketType.CLIENT); Socket server = ctx.createSocket(SocketType.SERVER)) { server.bind("inproc://zframe-test.routing"); client.connect("inproc://zframe-test.routing"); // Send request from CLIENT to SERVER ZFrame request = new ZFrame("Hello"); boolean rc = request.send(client, 0); Assert.assertTrue(rc); // Read request and send reply request = ZFrame.recvFrame(server); int routingId = request.getRoutingId(); String data = new String(request.getData()); Assert.assertEquals("Hello", data); Assert.assertNotEquals(0, routingId); ZFrame reply = new ZFrame("World"); reply.setRoutingId(routingId); rc = reply.send(server, 0); Assert.assertTrue(rc); // Read reply reply = ZFrame.recvFrame(client); data = new String(reply.getData()); Assert.assertEquals("World", data); Assert.assertEquals(0, reply.getRoutingId()); // Client Server Disallow Multipart ZFrame frame = new ZFrame("Hello"); rc = frame.send(client, ZFrame.MORE); Assert.assertEquals(false, rc); rc = frame.send(server, ZFrame.MORE); Assert.assertEquals(false, rc); } } } jeromq-0.6.0/src/test/java/org/zeromq/ZLoopTest.java000066400000000000000000000132251455771126300223710ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.After; //import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.zeromq.ZMQ.PollItem; import org.zeromq.ZMQ.Poller; import org.zeromq.ZMQ.Socket; public class ZLoopTest { private String received; private ZContext ctx; private Socket input; private Socket output; @Before public void setUp() { ctx = new ZContext(); assertThat(ctx, notNullValue()); output = ctx.createSocket(SocketType.PAIR); assertThat(output, notNullValue()); output.bind("inproc://zloop.test"); input = ctx.createSocket(SocketType.PAIR); assertThat(input, notNullValue()); input.connect("inproc://zloop.test"); received = "FAILED"; } @After public void tearDown() { ctx.destroy(); } @Test public void testZLoop() { int rc; ZLoop loop = new ZLoop(ctx); assertThat(loop, notNullValue()); ZLoop.IZLoopHandler timerEvent = (loop12, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop1, item, arg) -> { received = ((Socket) arg).recvStr(0); // Just end the reactor return -1; }; // After 10 msecs, send a ping message to output loop.addTimer(10, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); assertThat(rc, is(0)); loop.start(); loop.removePoller(pollInput); assertThat(received, is("PING")); } @Test public void testZLoopAddTimerFromTimer() { int rc; ZLoop loop = new ZLoop(ctx); assertThat(loop, notNullValue()); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { final long now = System.currentTimeMillis(); ZLoop.IZLoopHandler timerEvent2 = (loop12, item1, arg1) -> { final long now2 = System.currentTimeMillis(); assertThat(now2 >= now + 10, is(true)); ((Socket) arg1).send("PING", 0); return 0; }; loop13.addTimer(10, 1, timerEvent2, arg); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop1, item, arg) -> { received = ((Socket) arg).recvStr(0); // Just end the reactor return -1; }; // After 10 msecs, fire a timer that registers // another timer that sends the ping message loop.addTimer(10, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); assertThat(rc, is(0)); loop.start(); loop.removePoller(pollInput); assertThat(received, is("PING")); } @Test(timeout = 1000) public void testZLoopAddTimerFromSocketHandler() { int rc; ZLoop loop = new ZLoop(ctx); assertThat(loop, notNullValue()); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop12, item, arg) -> { final long now = System.currentTimeMillis(); ZLoop.IZLoopHandler timerEvent2 = (loop1, item1, arg1) -> { final long now2 = System.currentTimeMillis(); assertThat(now2 >= now + 10, is(true)); received = ((Socket) arg1).recvStr(0); // Just end the reactor return -1; }; // After 10 msec fire a timer that ends the reactor loop12.addTimer(10, 1, timerEvent2, arg); return 0; }; // Fire a timer that sends the ping message loop.addTimer(0, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); assertThat(rc, is(0)); loop.start(); loop.removePoller(pollInput); assertThat(received, is("PING")); } @Test(timeout = 1000) public void testZLoopEndReactorFromTimer() { int rc; ZLoop loop = new ZLoop(ctx); assertThat(loop, notNullValue()); ZLoop.IZLoopHandler timerEvent = (loop13, item, arg) -> { ((Socket) arg).send("PING", 0); return 0; }; ZLoop.IZLoopHandler socketEvent = (loop12, item, arg) -> { // After 10 msecs, fire an event that ends the reactor ZLoop.IZLoopHandler shutdownEvent = (loop1, item1, arg1) -> { received = ((Socket) arg1).recvStr(0); // Just end the reactor return -1; }; loop12.addTimer(10, 1, shutdownEvent, arg); return 0; }; // Fire event that sends a ping message to output loop.addTimer(0, 1, timerEvent, input); // When we get the ping message, end the reactor PollItem pollInput = new PollItem(output, Poller.POLLIN); rc = loop.addPoller(pollInput, socketEvent, output); assertThat(rc, is(0)); loop.start(); loop.removePoller(pollInput); assertThat(received, is("PING")); } } jeromq-0.6.0/src/test/java/org/zeromq/ZMonitorTest.java000066400000000000000000000276041455771126300231150ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.zeromq.ZAuth.ZapRequest; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMonitor.Event; import org.zeromq.ZMonitor.ZEvent; import zmq.io.mechanism.curve.Curve; public class ZMonitorTest { @Test public void testZMonitorImpossibleWorkflows() { final ZContext ctx = new ZContext(); final Socket socket = ctx.createSocket(SocketType.DEALER); final ZMonitor monitor = new ZMonitor(ctx, socket); // impossible to monitor events before being started ZEvent event = monitor.nextEvent(); Assert.assertNull(event); event = monitor.nextEvent(-1); Assert.assertNull(event); monitor.start(); // all no-ops commands once ZMonitor is started monitor.add().remove().verbose(false).start(); socket.close(); monitor.close(); ctx.close(); } @Test public void testZMonitor() { final ZContext ctx = new ZContext(); final Socket client = ctx.createSocket(SocketType.DEALER); final Socket server = ctx.createSocket(SocketType.DEALER); final ZMonitor clientMonitor = new ZMonitor(ctx, client); clientMonitor.verbose(true); clientMonitor.add(Event.LISTENING, Event.CONNECTED, Event.DISCONNECTED, Event.ACCEPT_FAILED); clientMonitor.remove(Event.ACCEPT_FAILED); clientMonitor.start(); final ZMonitor serverMonitor = new ZMonitor(ctx, server); serverMonitor.verbose(false); serverMonitor.add(Event.LISTENING, Event.ACCEPTED, Event.HANDSHAKE_PROTOCOL); serverMonitor.start(); // Check server is now listening int port = server.bindToRandomPort("tcp://127.0.0.1"); ZEvent received = serverMonitor.nextEvent(); Assert.assertEquals(Event.LISTENING, received.type); // Check server connected to client boolean rc = client.connect("tcp://127.0.0.1:" + port); Assert.assertTrue(rc); received = clientMonitor.nextEvent(); assertThat(received.type, is(Event.CONNECTED)); // Check server accepted connection received = serverMonitor.nextEvent(true); Assert.assertEquals(Event.ACCEPTED, received.type); // Check server accepted connection received = serverMonitor.nextEvent(-1); // timeout -1 aka blocking Assert.assertEquals(Event.HANDSHAKE_PROTOCOL, received.type); received = serverMonitor.nextEvent(false); // with no blocking Assert.assertNull(received); received = serverMonitor.nextEvent(10); // timeout Assert.assertNull(received); client.close(); clientMonitor.close(); server.close(); serverMonitor.close(); ctx.close(); } @Test(timeout = 5000) public void testZMonitorCurveOK() { final List receivedEventsClient = new ArrayList<>(); final List receivedEventsServer = new ArrayList<>(); final byte[][] serverKeyPair = new Curve().keypair(); final byte[] serverPublicKey = serverKeyPair[0]; final byte[] serverSecretKey = serverKeyPair[1]; final byte[][] clientKeyPair = new Curve().keypair(); final byte[] clientPublicKey = clientKeyPair[0]; final byte[] clientSecretKey = clientKeyPair[1]; final ZContext ctx = new ZContext(); final Socket client = ctx.createSocket(SocketType.PUSH); client.setCurveServerKey(serverPublicKey); client.setCurvePublicKey(clientPublicKey); client.setCurveSecretKey(clientSecretKey); final Socket server = ctx.createSocket(SocketType.PULL); server.setCurveServer(true); server.setCurveSecretKey(serverSecretKey); final ZMonitor clientMonitor = new ZMonitor(ctx, client); clientMonitor.verbose(true); clientMonitor.add(Event.ALL); clientMonitor.start(); final ZMonitor serverMonitor = new ZMonitor(ctx, server); serverMonitor.verbose(true); serverMonitor.add(Event.ALL); serverMonitor.start(); // Check server is now listening int port = server.bindToRandomPort("tcp://127.0.0.1"); // Check server connected to client boolean rc = client.connect("tcp://127.0.0.1:" + port); client.send("hello"); server.recvStr(); Assert.assertTrue(rc); client.close(); ZEvent received; while ((received = clientMonitor.nextEvent(100)) != null) { receivedEventsClient.add(received); } clientMonitor.close(); server.close(); while ((received = serverMonitor.nextEvent(100)) != null) { receivedEventsServer.add(received); } serverMonitor.close(); ctx.close(); // [ZEvent [_PROTOCOL, code=32768, address=tcp://127.0.0.1:53682, value=3], ZEvent [type=DISCONNECTED, code=512, address=tcp://127.0.0.1:53682, value=null]] final Event[] expectedEventsClient = new Event[] { Event.CONNECT_DELAYED, Event.CONNECTED, Event.HANDSHAKE_PROTOCOL, Event.MONITOR_STOPPED, }; check(receivedEventsClient, expectedEventsClient); final Event[] expectedEventsServer = new Event[] { Event.LISTENING, Event.ACCEPTED, Event.HANDSHAKE_PROTOCOL, Event.DISCONNECTED, Event.CLOSED, Event.MONITOR_STOPPED, }; check(receivedEventsServer, expectedEventsServer); } @Test(timeout = 5000) public void testZMonitorCurveKo() throws InterruptedException { final List receivedEventsClient = new ArrayList<>(); final List receivedEventsServer = new ArrayList<>(); final byte[][] serverKeyPair = new Curve().keypair(); final byte[] serverPublicKey = serverKeyPair[0]; final byte[] serverSecretKey = serverKeyPair[1]; final byte[][] clientKeyPair = new Curve().keypair(); final byte[] clientPublicKey = clientKeyPair[0]; final byte[] clientSecretKey = clientKeyPair[1]; final ZContext ctx = new ZContext(); final Socket client = ctx.createSocket(SocketType.PUSH); client.setCurveServerKey(serverPublicKey); client.setCurvePublicKey(clientPublicKey); client.setCurveSecretKey(serverSecretKey); final Socket server = ctx.createSocket(SocketType.PULL); server.setCurveServer(true); server.setCurveSecretKey(clientSecretKey); final ZMonitor clientMonitor = new ZMonitor(ctx, client); clientMonitor.verbose(true); clientMonitor.add(Event.ALL); clientMonitor.start(); final ZMonitor serverMonitor = new ZMonitor(ctx, server); serverMonitor.verbose(true); serverMonitor.add(Event.ALL); serverMonitor.start(); // Check server is now listening server.bind("tcp://127.0.0.1:34782"); // Check server connected to client boolean rc = client.connect("tcp://127.0.0.1:" + 34782); Assert.assertTrue(rc); Thread.sleep(100); client.send("hello"); Thread.sleep(100); client.close(); ZEvent received; while ((received = clientMonitor.nextEvent(100)) != null) { receivedEventsClient.add(received); } clientMonitor.close(); server.close(); while ((received = serverMonitor.nextEvent(100)) != null) { receivedEventsServer.add(received); } serverMonitor.close(); ctx.close(); final Event[] expectedEventsClient = new Event[] { Event.CONNECT_DELAYED, Event.CONNECTED, Event.HANDSHAKE_PROTOCOL, Event.DISCONNECTED, Event.MONITOR_STOPPED, }; check(receivedEventsClient, expectedEventsClient); final Event[] expectedEventsServer = new Event[] { Event.LISTENING, Event.ACCEPTED, Event.HANDSHAKE_PROTOCOL, Event.HANDSHAKE_FAILED_PROTOCOL, Event.DISCONNECTED, Event.CLOSED, Event.MONITOR_STOPPED, }; check(receivedEventsServer, expectedEventsServer); } @Test(timeout = 5000) public void testPlainKo() { final List receivedEventsClient = new ArrayList<>(); final List receivedEventsServer = new ArrayList<>(); ZAuth.Auth plain = new ZAuth.Auth() { @Override public boolean authorize(ZapRequest request, boolean verbose) { return false; } @Override public boolean configure(ZMsg msg, boolean verbose) { return true; } }; try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, "authenticator", Collections.singletonMap("PLAIN", plain)); Socket server = ctx.createSocket(SocketType.PUSH); ZMonitor serverMonitor = new ZMonitor(ctx, server); Socket client = ctx.createSocket(SocketType.PULL); ZMonitor clientMonitor = new ZMonitor(ctx, client)) { clientMonitor.verbose(true); clientMonitor.add(Event.ALL); clientMonitor.start(); serverMonitor.verbose(true); serverMonitor.add(Event.ALL); serverMonitor.start(); server.setPlainServer(true); // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(true); // auth send the replies auth.replies(true); // Create and bind server socket server.setPlainServer(true); server.setZapDomain("global".getBytes()); final int port = server.bindToRandomPort("tcp://*"); // Create and connect client socket client.setPlainUsername("admin".getBytes()); client.setPlainPassword("wrong".getBytes()); boolean rc = client.connect("tcp://127.0.0.1:" + port); Assert.assertTrue(rc); // Send a single message from server to client rc = server.send("Hello"); Assert.assertTrue(rc); ZAuth.ZapReply reply = auth.nextReply(); Assert.assertEquals(400, reply.statusCode); ZEvent received; while ((received = clientMonitor.nextEvent(100)) != null) { receivedEventsClient.add(received); } while ((received = serverMonitor.nextEvent(100)) != null) { receivedEventsServer.add(received); } final Event[] expectedEventsClient = new Event[] { Event.CONNECT_DELAYED, Event.CONNECTED, Event.HANDSHAKE_PROTOCOL, Event.HANDSHAKE_FAILED_AUTH, Event.DISCONNECTED, }; check(receivedEventsClient, expectedEventsClient); final Event[] expectedEventsServer = new Event[] { Event.LISTENING, Event.ACCEPTED, Event.HANDSHAKE_PROTOCOL, Event.DISCONNECTED, }; check(receivedEventsServer, expectedEventsServer); } } private void check(final List receivedEvents, final Event[] expectedEvents) { Assert.assertEquals(expectedEvents.length, receivedEvents.size()); for (int i = 0; i < expectedEvents.length; i++) { Assert.assertEquals(expectedEvents[i], receivedEvents.get(i).type); } } } jeromq-0.6.0/src/test/java/org/zeromq/ZMsgTest.java000066400000000000000000000275671455771126300222240ustar00rootroot00000000000000package org.zeromq; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Iterator; import org.junit.Test; import org.zeromq.ZMQ.Socket; import zmq.ZError; /** * Created by hartmann on 3/21/14. */ public class ZMsgTest { @Test public void testRecvFrame() { ZMQ.Context ctx = ZMQ.context(0); ZMQ.Socket socket = ctx.socket(SocketType.PULL); ZFrame frame = ZFrame.recvFrame(socket, ZMQ.NOBLOCK); assertThat(frame, nullValue()); socket.close(); ctx.close(); } @Test public void testRecvMsg() { ZMQ.Context ctx = ZMQ.context(0); ZMQ.Socket socket = ctx.socket(SocketType.PULL); ZMsg.recvMsg(socket, ZMQ.NOBLOCK, (msg)-> assertThat(msg, nullValue())); socket.close(); ctx.close(); } @Test public void testRecvNullByteMsg() { ZMQ.Context ctx = ZMQ.context(0); ZMQ.Socket sender = ctx.socket(SocketType.PUSH); ZMQ.Socket receiver = ctx.socket(SocketType.PULL); receiver.bind("inproc://" + this.hashCode()); sender.connect("inproc://" + this.hashCode()); sender.send(new byte[0]); ZMsg msg = ZMsg.recvMsg(receiver, ZMQ.NOBLOCK); assertThat(msg, notNullValue()); sender.close(); receiver.close(); ctx.close(); } @Test public void testContentSize() { ZMsg msg = new ZMsg(); msg.add(new byte[0]); assertThat(msg.contentSize(), is(0L)); msg.add(new byte[1]); assertThat(msg.contentSize(), is(1L)); } @Test public void testEquals() { ZMsg msg = new ZMsg().addString("123"); ZMsg other = new ZMsg(); assertThat(msg.equals(msg), is(true)); assertThat(msg.equals(null), is(false)); assertThat(msg.equals(""), is(false)); assertThat(msg.equals(other), is(false)); other.add("345"); assertThat(msg.equals(other), is(false)); other.popString(); other.add("123"); assertThat(msg.equals(other), is(true)); assertThat(msg.duplicate(), is(msg)); msg.destroy(); } @Test public void testHashcode() { ZMsg msg = new ZMsg(); assertThat(msg.hashCode(), is(0)); msg.add("123"); ZMsg other = ZMsg.newStringMsg("123"); assertThat(msg.hashCode(), is(other.hashCode())); other = new ZMsg().append("2"); assertThat(msg.hashCode(), is(not(equalTo(other.hashCode())))); } @Test public void testDump() { ZMsg msg = new ZMsg().append(new byte[0]).append(new byte[] { (byte) 0xAA }); msg.dump(); StringBuilder out = new StringBuilder(); msg.dump(out); System.out.println(msg); assertThat(out.toString(), endsWith("[000] \n[001] AA\n")); } @Test public void testWrapUnwrap() { ZMsg msg = new ZMsg().wrap(new ZFrame("456")); assertThat(msg.size(), is(2)); ZFrame frame = msg.unwrap(); assertThat(frame.toString(), is("456")); assertThat(msg.size(), is(0)); } @Test public void testSaveLoad() { ZMsg msg = new ZMsg().append("123"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(stream); ZMsg.save(msg, out); DataInputStream in = new DataInputStream(new ByteArrayInputStream(stream.toByteArray())); ZMsg loaded = ZMsg.load(in); assertThat(msg, is(loaded)); } @Test public void testAppend() { ZMsg msg = new ZMsg().append((ZMsg) null).append(ZMsg.newStringMsg("123")); assertThat(msg.popString(), is("123")); msg.append(ZMsg.newStringMsg("123")).append(msg); assertThat(msg.size(), is(2)); assertThat(msg.contentSize(), is(6L)); } @Test public void testPop() { ZMsg msg = new ZMsg(); assertThat(msg.popString(), nullValue()); } @Test public void testRemoves() { ZMsg msg = ZMsg.newStringMsg("1", "2", "3", "4"); assertThat(msg.remove(), is(new ZFrame("1"))); assertThat(msg.removeLast(), is(new ZFrame("4"))); assertThat(msg.removeFirst(), is(new ZFrame("2"))); msg.add(new ZFrame("5")); msg.add(new ZFrame("6")); msg.add(new ZFrame("7")); assertThat(msg.size(), is(4)); boolean removed = msg.removeFirstOccurrence(new ZFrame("5")); assertThat(removed, is(true)); removed = msg.removeFirstOccurrence(new ZFrame("5")); assertThat(removed, is(false)); removed = msg.removeFirstOccurrence(new ZFrame("7")); assertThat(removed, is(true)); assertThat(msg.size(), is(2)); msg.add(new ZFrame("6")); msg.add(new ZFrame("4")); msg.add(new ZFrame("6")); removed = msg.removeLastOccurrence(new ZFrame("6")); assertThat(removed, is(true)); assertThat(msg.size(), is(4)); } @Test public void testMessageEquals() { ZMsg msg = new ZMsg(); ZFrame hello = new ZFrame("Hello"); ZFrame world = new ZFrame("World"); msg.add(hello); msg.add(world); assertThat(msg, is(msg.duplicate())); ZMsg reverseMsg = new ZMsg(); msg.add(hello); msg.addFirst(world); assertThat(msg.equals(reverseMsg), is(false)); } @Test public void testSingleFrameMessage() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zmsg.test"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zmsg.test"); // Test send and receive of a single ZMsg ZMsg msg = new ZMsg(); ZFrame frame = new ZFrame("Hello"); msg.addFirst(frame); assertThat(msg.size(), is(1)); assertThat(msg.contentSize(), is(5L)); msg.send(output); ZMsg msg2 = ZMsg.recvMsg(input); assertThat(msg2, notNullValue()); assertThat(msg2.size(), is(1)); assertThat(msg2.contentSize(), is(5L)); msg.destroy(); msg2.destroy(); ctx.close(); } @Test public void testMultiPart() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zmsg.test2"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zmsg.test2"); ZMsg msg = new ZMsg(); for (int i = 0; i < 10; i++) { msg.addString("Frame" + i); } ZMsg copy = msg.duplicate(); copy.send(output); msg.send(output); copy = ZMsg.recvMsg(input); assertThat(copy, notNullValue()); assertThat(copy.size(), is(10)); assertThat(copy.contentSize(), is(60L)); copy.destroy(); msg = ZMsg.recvMsg(input); assertThat(msg, notNullValue()); assertThat(msg.size(), is(10)); int count = 0; for (ZFrame f : msg) { assertThat(f.streq("Frame" + count++), is(true)); } assertThat(msg.contentSize(), is(60L)); msg.destroy(); ctx.close(); } @Test public void testMessageFrameManipulation() { ZMsg msg = new ZMsg(); for (int i = 0; i < 10; i++) { msg.addString("Frame" + i); } // Remove all frames apart from the first and last one for (int i = 0; i < 8; i++) { Iterator iter = msg.iterator(); iter.next(); // Skip first frame ZFrame f = iter.next(); msg.remove(f); f.destroy(); } assertThat(msg.size(), is(2)); assertThat(msg.contentSize(), is(12L)); assertThat(msg.getFirst().streq("Frame0"), is(true)); assertThat(msg.getLast().streq("Frame9"), is(true)); ZFrame f = new ZFrame("Address"); msg.push(f); assertThat(msg.size(), is(3)); assertThat(msg.getFirst().streq("Address"), is(true)); msg.addString("Body"); assertThat(msg.size(), is(4)); ZFrame f0 = msg.pop(); assertThat(f0.streq("Address"), is(true)); msg.destroy(); msg = new ZMsg(); f = new ZFrame("Address"); msg.wrap(f); assertThat(msg.size(), is(2)); msg.addString("Body"); assertThat(msg.size(), is(3)); f = msg.unwrap(); f.destroy(); assertThat(msg.size(), is(1)); msg.destroy(); } @Test public void testEmptyMessage() { ZMsg msg = new ZMsg(); assertThat(msg.size(), is(0)); assertThat(msg.getFirst(), nullValue()); assertThat(msg.getLast(), nullValue()); assertThat(msg.isEmpty(), is(true)); assertThat(msg.pop(), nullValue()); assertThat(msg.removeFirst(), nullValue()); assertThat(msg.removeFirstOccurrence(null), is(false)); assertThat(msg.removeLast(), nullValue()); msg.destroy(); } @Test public void testLoadSave() { ZMsg msg = new ZMsg(); for (int i = 0; i < 10; i++) { msg.addString("Frame" + i); } try { // Save msg to a file File f = new File(TemporaryFolderFinder.resolve("zmsg.test")); DataOutputStream dos = new DataOutputStream(Files.newOutputStream(f.toPath())); assertThat(ZMsg.save(msg, dos), is(true)); dos.close(); // Read msg out of the file DataInputStream dis = new DataInputStream(Files.newInputStream(f.toPath())); ZMsg msg2 = ZMsg.load(dis); dis.close(); f.delete(); assertThat(msg2.size(), is(10)); assertThat(msg2.contentSize(), is(60L)); } catch (IOException e) { e.printStackTrace(); fail(); } } @Test public void testNewStringMessage() { // A single string => frame ZMsg msg = ZMsg.newStringMsg("Foo"); assertThat(msg.size(), is(1)); assertThat(msg.getFirst().streq("Foo"), is(true)); // Multiple strings => frames ZMsg msg2 = ZMsg.newStringMsg("Foo", "Bar", "Baz"); assertThat(msg2.size(), is(3)); assertThat(msg2.getFirst().streq("Foo"), is(true)); assertThat(msg2.getLast().streq("Baz"), is(true)); // Empty message (Not very useful) ZMsg msg3 = ZMsg.newStringMsg(); assertThat(msg3.isEmpty(), is(true)); } @Test public void testClosedContext() { ZContext ctx = new ZContext(); Socket output = ctx.createSocket(SocketType.PAIR); output.bind("inproc://zmsg.test"); Socket input = ctx.createSocket(SocketType.PAIR); input.connect("inproc://zmsg.test"); ZMsg msg = ZMsg.newStringMsg("Foo", "Bar"); msg.send(output); ZMsg msg2 = ZMsg.recvMsg(input); assertThat(msg2.popString(), is("Foo")); assertThat(msg2.popString(), is("Bar")); msg2.destroy(); msg.send(output); msg.destroy(); ctx.close(); try { ZMsg.recvMsg(input); fail(); } catch (ZMQException e) { assertThat(e.getErrorCode(), anyOf(is(ZError.ETERM), is(ZError.EINTR))); } } } jeromq-0.6.0/src/test/java/org/zeromq/ZSocketTest.java000066400000000000000000000016541455771126300227130ustar00rootroot00000000000000package org.zeromq; import static org.junit.Assert.assertEquals; import java.io.IOException; import org.junit.Test; public class ZSocketTest { @Test public void pushPullTest() throws IOException { int port = Utils.findOpenPort(); try ( final ZSocket pull = new ZSocket(SocketType.PULL); final ZSocket push = new ZSocket(SocketType.PUSH)) { pull.bind("tcp://*:" + port); push.connect("tcp://127.0.0.1:" + port); final String expected = "hello"; push.sendStringUtf8(expected); final String actual = pull.receiveStringUtf8(); assertEquals(expected, actual); assertEquals(SocketType.PULL, pull.getSocketType()); assertEquals(ZMQ.PULL, pull.getType()); assertEquals(SocketType.PUSH, push.getSocketType()); assertEquals(ZMQ.PUSH, push.getType()); } } } jeromq-0.6.0/src/test/java/org/zeromq/auth/000077500000000000000000000000001455771126300205615ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/auth/TestUtils.java000066400000000000000000000013751455771126300233720ustar00rootroot00000000000000package org.zeromq.auth; import java.io.File; public class TestUtils { private TestUtils() { } /** * Remove a directory and delete all files and sub dirs recursively * CAUTION: beware of symbolic links. not tested how the behavior is but suspect this function will follow those as well. * @param path as String */ public static void cleanupDir(String path) { cleanupDir(new File(path)); } private static void cleanupDir(File path) { if (!path.exists()) { return; } if (path.isDirectory()) { for (File file : path.listFiles()) { cleanupDir(file); file.delete(); } } path.delete(); } } jeromq-0.6.0/src/test/java/org/zeromq/auth/ZAuthTest.java000066400000000000000000000544551455771126300233340ustar00rootroot00000000000000package org.zeromq.auth; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.zeromq.SocketType; import org.zeromq.ZAuth; import org.zeromq.ZCert; import org.zeromq.ZCertStore; import org.zeromq.ZContext; import org.zeromq.ZMQ; import java.io.FileWriter; import java.io.IOException; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; /** * Test are basically the java-ports based on Using ZeroMQ Security (part 2) */ public class ZAuthTest { private static final boolean VERBOSE_MODE = true; @Rule public final TemporaryFolder folder = new TemporaryFolder(); private String certificateFolder; private String passwordsFile; @Before public void init() throws IOException { // create test-passwords passwordsFile = folder.newFile().getPath(); certificateFolder = folder.newFolder().getPath(); try (FileWriter write = new FileWriter(passwordsFile)) { write.write("guest=guest\n"); write.write("tourist=1234\n"); write.write("admin=secret\n"); } } @Test(timeout = 5000) public void testNull() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx)) { // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Create and bind server socket ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); server.setZapDomain("test"); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket ZMQ.Socket client = ctx.createSocket(SocketType.PULL); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(); assertThat(reply.statusCode, is(200)); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testNullAllowed() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx)) { // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); auth.allow("127.0.0.1"); // Create and bind server socket ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); server.setZapDomain("test"); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket ZMQ.Socket client = ctx.createSocket(SocketType.PULL); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(); assertThat(reply.statusCode, is(200)); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testNullWithNoDomain() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx)) { // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); auth.allow("127.0.0.1"); // Create and bind server socket ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket ZMQ.Socket client = ctx.createSocket(SocketType.PULL); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(1000); assertThat(reply, nullValue()); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testPlainWithPassword() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); auth.configurePlain("*", passwordsFile); // Create and bind server socket ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); server.setPlainServer(true); server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket ZMQ.Socket client = ctx.createSocket(SocketType.PULL); client.setPlainUsername("admin".getBytes()); client.setPlainPassword("secret".getBytes()); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(); assertThat(reply.statusCode, is(200)); assertThat(reply.userId, is("admin")); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testPlainWithPasswordDenied() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(true); // auth send the replies auth.replies(true); auth.configurePlain("*", passwordsFile); // Create and bind server socket server.setPlainServer(true); server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket client.setPlainUsername("admin".getBytes()); client.setPlainPassword("wrong".getBytes()); // Create and connect client socket rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(); assertThat(reply.statusCode, is(400)); } } @Test(timeout = 5000) public void testCurveAnyClient() { // accept any client-certificate try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); auth.configureCurve(ZAuth.CURVE_ALLOW_ANY); // We need two certificates, one for the client and one for // the server. The client must know the server's public key // to make a CURVE connection. ZCert clientCert = new ZCert(); ZCert serverCert = new ZCert(); // Create and bind server socket server.setZapDomain("global".getBytes()); server.setCurveServer(true); serverCert.apply(server); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket clientCert.apply(client); client.setCurveServerKey(serverCert.getPublicKey()); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(); assertThat(reply.statusCode, is(200)); assertThat(reply.userId, is("")); System.out.println("ZAuth replied " + reply); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testCurveSuccessful() throws IOException { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Tell authenticator to use the certificate store in .curve auth.configureCurve(certificateFolder); // We'll generate a new client certificate and save the public part // in the certificate store (in practice this would be done by hand // or some out-of-band process). ZCert clientCert = new ZCert(); clientCert.setMeta("name", "Client test certificate"); clientCert.setMeta("meta1/meta2/meta3", "Third level of meta"); // wait a second before overwriting a cert, otherwise the certstore won't see that the file actually changed and will deny // if creating new files, this is not needed // ZMQ.sleep(1); clientCert.savePublic(certificateFolder + "/testcert.pub"); ZCert serverCert = new ZCert(); // Create and bind server socket server.setZapDomain("global".getBytes()); server.setCurveServer(true); server.setCurvePublicKey(serverCert.getPublicKey()); server.setCurveSecretKey(serverCert.getSecretKey()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket client.setCurvePublicKey(clientCert.getPublicKey()); client.setCurveSecretKey(clientCert.getSecretKey()); client.setCurveServerKey(serverCert.getPublicKey()); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(200)); assertThat(reply.userId, is(clientCert.getPublicKeyAsZ85())); String message = client.recvStr(); assertThat(message, is("Hello")); } } @Test(timeout = 5000) public void testBlacklistDenied() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Timestamper()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Blacklist our address auth.deny("127.0.0.1"); // Create and bind server socket server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(400)); assertThat(reply.userId, is("")); } } @Test(timeout = 5000) public void testBlacklistAllowed() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Blacklist another address auth.deny("127.0.0.2"); // Create and bind server socket server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(200)); assertThat(reply.userId, is("")); } } @Test(timeout = 5000) public void testWhitelistDenied() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Whitelist another address auth.allow("127.0.0.2"); // Create and bind server socket server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(400)); assertThat(reply.userId, is("")); } } @Test(timeout = 5000) public void testWhitelistAllowed() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Whitelist our address auth.allow("127.0.0.1"); // Create and bind server socket server.setZapDomain("global".getBytes()); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(200)); assertThat(reply.userId, is("")); } } @Test(timeout = 5000) public void testCurveFail() { // this is the same test but here we do not save the client's certificate into the certstore's folder try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Timestamper()); ZMQ.Socket server = ctx.createSocket(SocketType.PUSH); ZMQ.Socket client = ctx.createSocket(SocketType.PULL)) { // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth send the replies auth.replies(true); // Tell authenticator to use the certificate store in .curve auth.configureCurve(certificateFolder); // We'll generate a new client certificate and save the public part // in the certificate store (in practice this would be done by hand // or some out-of-band process). ZCert clientCert = new ZCert(); clientCert.setMeta("name", "Client test certificate"); // HERE IS THE PROBLEM. Not client-certificate means that the client will be rejected // client_cert.savePublic(CERTIFICATE_FOLDER+"/testcert.pub"); ZCert serverCert = new ZCert(); // Create and bind server socket server.setZapDomain("global".getBytes()); server.setCurveServer(true); serverCert.apply(server); boolean rc = server.bind("tcp://localhost:*"); assertThat(rc, is(true)); // Create and connect client socket clientCert.apply(client); client.setCurveServerKey(serverCert.getPublicKey()); rc = client.connect(server.getLastEndpoint()); assertThat(rc, is(true)); // add a timeout so that the client won't wait forever (since it is not connected) rc = client.setReceiveTimeOut(100); assertThat(rc, is(true)); // By default PUSH sockets block if there's no peer rc = server.setSendTimeOut(200); assertThat(rc, is(true)); // Send a single message from server to client server.send("Hello"); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply.statusCode, is(400)); // the timeout will leave the recvStr-method with null as result String message = client.recvStr(); assertThat(message, nullValue()); } } @Test(timeout = 5000) public void testNoReplies() { try (ZContext ctx = new ZContext(); ZAuth auth = new ZAuth(ctx, new ZCertStore.Hasher())) { // Start an authentication engine for this context. This engine // allows or denies incoming connections (talking to the libzmq // core over a protocol called ZAP). // Get some indication of what the authenticator is deciding auth.setVerbose(VERBOSE_MODE); // auth do not send the replies auth.replies(false); ZAuth.ZapReply reply = auth.nextReply(true); assertThat(reply, nullValue()); reply = auth.nextReply(1); assertThat(reply, nullValue()); } } } jeromq-0.6.0/src/test/java/org/zeromq/auth/ZConfigTest.java000066400000000000000000000161041455771126300236250ustar00rootroot00000000000000package org.zeromq.auth; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.zeromq.TemporaryFolderFinder; import org.zeromq.ZConfig; public class ZConfigTest { private static final String TEST_FOLDER = "target/testCertFolder"; private static final ZConfig conf = new ZConfig("root", null); private String testFolder = TEST_FOLDER; @Before public void init() throws IOException { // create test-passwords testFolder = TemporaryFolderFinder.resolve(TEST_FOLDER); conf.putValue("/curve/public-key", "abcdefg"); conf.putValue("/curve/secret-key", "(w3lSF/5yv&j*c&0h{4JHe(CETJSksTr.QSjcZE}"); conf.putValue("metadata/name", "key-value tests"); // create test-file with values that should be compatible but are actually not created with this implementation File dir = new File(testFolder); if (!dir.exists()) { dir.mkdir(); } FileWriter write = new FileWriter(testFolder + "/test.zpl"); write.write("1. ZPL configuration file example\n"); // should be discarded write.write(" # some initial comment \n"); // should be discarded write.write("meta\n"); write.write(" leadingquote = \"abcde\n"); write.write(" endingquote = abcde\"\n"); write.write(" quoted = \"abcde\"\n"); write.write(" singlequoted = 'abcde'\n"); write.write(" bind = tcp://eth0:5555\n"); write.write(" verbose = 1 # Ask for a trace\n"); write.write(" sub # some comment after container-name\n"); write.write(" fortuna = f95\n"); write.close(); write = new FileWriter(testFolder + "/reference.zpl"); write.write("context\n"); write.write(" iothreads = 1\n"); write.write(" verbose = 1 # Ask for a trace\n"); write.write("main\n"); write.write(" type = zqueue # ZMQ_DEVICE type\n"); write.write(" frontend\n"); write.write(" option\n"); write.write(" hwm = 1000\n"); write.write(" swap = 25000000 # 25MB\n"); write.write(" bind = 'inproc://addr1'\n"); write.write(" bind = 'ipc://addr2'\n"); write.write(" backend\n"); write.write(" bind = inproc://addr3\n"); write.close(); } @Test public void testPutKeyDoubleSlash() { ZConfig config = new ZConfig("root", null); config.putValue("inproc://test", "one"); assertThat(config.pathExists("inproc://test"), is(true)); } @Test public void testPutKeySingleSlash() { ZConfig config = new ZConfig("root", null); config.putValue("server/timeout", "1000"); assertThat(config.pathExists("server/timeout"), is(true)); } @Test public void testGetKeySingleSlash() { ZConfig config = new ZConfig("root", null); config.putValue("server/timeout", "1000"); Map values = config.getValues(); assertThat(values.toString(), values.size(), is(1)); assertThat(values.toString(), values.containsKey("server/timeout"), is(true)); } @Test @Ignore public void testGetKeyDoubleSlash() { ZConfig config = new ZConfig("root", null); config.putValue("inproc://test", "one"); Map values = config.getValues(); assertThat(values.toString(), values.size(), is(1)); assertThat(values.toString(), values.containsKey("inproc://test"), is(true)); } @Test public void testPutGet() { assertThat(conf.getValue("/curve/public-key"), is("abcdefg")); // intentionally checking without leading / assertThat(conf.getValue("curve/secret-key"), is("(w3lSF/5yv&j*c&0h{4JHe(CETJSksTr.QSjcZE}")); assertThat(conf.getValue("/metadata/name"), is("key-value tests")); // checking default value assertThat(conf.getValue("/metadata/nothinghere", "default"), is("default")); } @Test public void testLoadSave() throws IOException { conf.save(testFolder + "/test.cert"); assertThat(isFileInPath(testFolder, "test.cert"), is(true)); ZConfig loadedConfig = ZConfig.load(testFolder + "/test.cert"); // Object obj = loadedConfig.getValue("/curve/public-key"); assertThat(loadedConfig.getValue("/curve/public-key"), is("abcdefg")); // intentionally checking without leading / assertThat(loadedConfig.getValue("curve/secret-key"), is("(w3lSF/5yv&j*c&0h{4JHe(CETJSksTr.QSjcZE}")); assertThat(loadedConfig.getValue("/metadata/name"), is("key-value tests")); } private boolean isFileInPath(String path, String filename) { File dir = new File(path); if (!dir.isDirectory()) { return false; } for (File file : dir.listFiles()) { if (file.getName().equals(filename)) { return true; } } return false; } @Test public void testZPLSpecialCases() throws IOException { // this file was generated in the init-method and tests some cases that should be processed by the loader but are not // created with our writer. ZConfig zplSpecials = ZConfig.load(testFolder + "/test.zpl"); // test leading quotes assertThat(zplSpecials.getValue("meta/leadingquote"), is("\"abcde")); // test ending quotes assertThat(zplSpecials.getValue("meta/endingquote"), is("abcde\"")); // test full doublequoted. here the quotes should be removed assertThat(zplSpecials.getValue("meta/quoted"), is("abcde")); // test full singlequoted. here the quotes should be removed assertThat(zplSpecials.getValue("meta/singlequoted"), is("abcde")); // test no quotes tcp-pattern assertThat(zplSpecials.getValue("meta/bind"), is("tcp://eth0:5555")); // test comment after value assertThat(zplSpecials.getValue("meta/verbose"), is("1")); // test comment after container-name assertThat(zplSpecials.pathExists("meta/sub"), is(true)); } @Test public void testReadReference() throws IOException { ZConfig ref = ZConfig.load(testFolder + "/reference.zpl"); assertThat(ref.getValue("context/iothreads"), is("1")); assertThat(ref.getValue("context/verbose"), is("1")); assertThat(ref.getValue("main/type"), is("zqueue")); assertThat(ref.getValue("main/frontend/option/hwm"), is("1000")); assertThat(ref.getValue("main/frontend/option/swap"), is("25000000")); assertThat(ref.getValue("main/frontend/bind"), is("ipc://addr2")); assertThat(ref.getValue("main/backend/bind"), is("inproc://addr3")); } @After public void cleanup() { TestUtils.cleanupDir(testFolder); } } jeromq-0.6.0/src/test/java/org/zeromq/guide/000077500000000000000000000000001455771126300207155ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/guide/AsyncServerTest.java000066400000000000000000000213341455771126300246670ustar00rootroot00000000000000package org.zeromq.guide; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.zeromq.SocketType; import org.zeromq.Utils; import org.zeromq.ZActor; import org.zeromq.ZContext; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMsg; import org.zeromq.ZPoller; import org.zeromq.ZProxy; import org.zeromq.ZProxy.Plug; import org.zeromq.timer.TimerHandler; import org.zeromq.timer.ZTimer; public class AsyncServerTest { private static final Random rand = new Random(System.nanoTime()); private static final AtomicInteger counter = new AtomicInteger(); //--------------------------------------------------------------------- //This is our client task //It connects to the server, and then sends a request once per second //It collects responses as they arrive, and it prints them out. We will //run several client tasks in parallel, each with a different random ID. private static class Client extends ZActor.SimpleActor implements TimerHandler { private int requestNbr = 0; private final String identity = String.format("%04X-%04X", counter.incrementAndGet(), rand.nextInt()); private final ZTimer timer = new ZTimer(); private ZTimer.Timer handle; private Socket client; private final int port; public Client(int port) { this.port = port; } @Override public List createSockets(ZContext ctx, Object... args) { client = ctx.createSocket(SocketType.DEALER); assertThat(client, notNullValue()); return Collections.singletonList(client); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { boolean rc = client.setIdentity(identity.getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); rc = client.connect("tcp://localhost:" + port); assertThat(rc, is(true)); rc = poller.register(client, ZPoller.POLLIN); assertThat(rc, is(true)); handle = timer.add(100, this); assertThat(handle, notNullValue()); } @Override public long looping(Socket pipe, ZPoller poller) { return timer.timeout(); } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { ZMsg msg = ZMsg.recvMsg(socket); assertThat(msg, notNullValue()); ZFrame content = msg.pop(); assertThat(content, notNullValue()); System.out.println(identity + " " + content); assertThat(content.getString(ZMQ.CHARSET), endsWith(identity)); return true; } @Override public boolean looped(Socket pipe, ZPoller poller) { int rc = timer.sleepAndExecute(); assertThat(rc, is(1)); return super.looped(pipe, poller); } @Override public void time(Object... args) { boolean rc = client.send(String.format("request #%02d - %s", ++requestNbr, identity)); assertThat(rc, is(true)); } @Override public boolean destroyed(ZContext ctx, Socket pipe, ZPoller poller) { boolean rc = handle.cancel(); assertThat(rc, is(true)); return super.destroyed(ctx, pipe, poller); } } //This is our server task. //It uses the multithreaded server model to deal requests out to a pool //of workers and route replies back to clients. One worker can handle //one request at a time but one client can talk to multiple workers at //once. private static class Proxy extends ZProxy.Proxy.SimpleProxy { private final int frontend; public Proxy(int frontend) { this.frontend = frontend; } @Override public Socket create(ZContext ctx, Plug place, Object... args) { switch (place) { case FRONT: Socket front = ctx.createSocket(SocketType.ROUTER); assertThat(front, notNullValue()); return front; case BACK: Socket back = ctx.createSocket(SocketType.DEALER); assertThat(back, notNullValue()); return back; default: return null; } } @Override public boolean configure(Socket socket, Plug place, Object... args) { switch (place) { case FRONT: return socket.bind("tcp://*:" + frontend); case BACK: return socket.bind("inproc://backend"); default: return true; } } } //Each worker task works on one request at a time and sends a random number //of replies back, with random delays between replies: private static class Worker extends ZActor.SimpleActor { private int counter = 0; private final int id; public Worker(int id) { this.id = id; } @Override public List createSockets(ZContext ctx, Object... args) { Socket socket = ctx.createSocket(SocketType.DEALER); assertThat(socket, notNullValue()); return Collections.singletonList(socket); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { Socket worker = sockets.get(0); boolean rc = worker.setLinger(0); assertThat(rc, is(true)); rc = worker.setReceiveTimeOut(100); assertThat(rc, is(true)); rc = worker.setSendTimeOut(100); assertThat(rc, is(true)); rc = worker.connect("inproc://backend"); assertThat(rc, is(true)); rc = poller.register(worker, ZPoller.IN); assertThat(rc, is(true)); } @Override public boolean stage(Socket worker, Socket pipe, ZPoller poller, int events) { ZMsg msg = ZMsg.recvMsg(worker); if (msg == null) { return false; } ZFrame address = msg.pop(); assertThat(address, notNullValue()); String request = msg.popString(); assertThat(request, notNullValue()); // Send 0..4 replies back final int replies = rand.nextInt(5); for (int reply = 0; reply < replies; reply++) { // Sleep for some fraction of a second ZMQ.msleep(rand.nextInt(1000) + 1); msg = new ZMsg(); boolean rc = msg.add(address); assertThat(rc, is(true)); msg.add(String.format("worker #%s reply #%02d : %s", id, ++counter, request)); rc = msg.send(worker); if (!rc) { return false; } } return true; } } //The main thread simply starts several clients, and a server, and then //waits for the server to finish. @Test(timeout = 10000) public void testAsyncServer() throws Exception { try ( final ZContext ctx = new ZContext()) { final int port = Utils.findOpenPort(); List actors = new ArrayList<>(); actors.add(new ZActor(new Client(port), null)); actors.add(new ZActor(new Client(port), null)); actors.add(new ZActor(new Client(port), null)); ZProxy proxy = ZProxy.newProxy(ctx, "proxy", new Proxy(port), null); String status = proxy.start(true); assertThat(status, is(ZProxy.STARTED)); // Launch pool of worker threads, precise number is not critical for (int threadNbr = 0; threadNbr < 5; threadNbr++) { actors.add(new ZActor(ctx, new Worker(threadNbr), null)); } ZMQ.sleep(1); status = proxy.pause(true); assertThat(status, is(ZProxy.PAUSED)); for (ZActor actor : actors) { boolean rc = actor.send("fini"); assertThat(rc, is(true)); actor.exit().awaitSilent(); } status = proxy.exit(); assertThat(status, is(ZProxy.EXITED)); } } } jeromq-0.6.0/src/test/java/org/zeromq/guide/EspressoTest.java000066400000000000000000000201341455771126300242230ustar00rootroot00000000000000package org.zeromq.guide; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; import org.junit.Test; import org.zeromq.SocketType; import org.zeromq.Utils; import org.zeromq.ZActor; import org.zeromq.ZContext; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Socket; import org.zeromq.ZPoller; import org.zeromq.ZProxy; import org.zeromq.ZProxy.Plug; // Espresso Pattern // This shows how to capture data using a pub-sub proxy public class EspressoTest { // The subscriber thread requests messages starting with // A and B, then reads and counts incoming messages. private static class Subscriber extends ZActor.SimpleActor { private final int port; private int count; private final CountDownLatch wait; public Subscriber(int port, CountDownLatch wait) { this.port = port; this.wait = wait; } @Override public List createSockets(ZContext ctx, Object... args) { Socket sub = ctx.createSocket(SocketType.SUB); assertThat(sub, notNullValue()); return Collections.singletonList(sub); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { Socket subscriber = sockets.get(0); boolean rc = subscriber.connect("tcp://localhost:" + port); assertThat(rc, is(true)); rc = subscriber.subscribe("A"); assertThat(rc, is(true)); rc = subscriber.subscribe("B".getBytes(ZMQ.CHARSET)); assertThat(rc, is(true)); rc = poller.register(subscriber, ZPoller.IN); assertThat(rc, is(true)); } @Override public String premiere(Socket pipe) { wait.countDown(); return "Subscriber"; } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { String string = socket.recvStr(); return string == null || count++ < 5; } } // .split publisher thread // The publisher sends random messages starting with A-J: private static class Publisher extends ZActor.SimpleActor { private final Random rand = new Random(System.currentTimeMillis()); private final int port; private int count; private final CountDownLatch wait; public Publisher(int port, CountDownLatch wait) { this.port = port; this.wait = wait; } @Override public List createSockets(ZContext ctx, Object... args) { Socket pub = ctx.createSocket(SocketType.PUB); assertThat(pub, notNullValue()); return Collections.singletonList(pub); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { Socket publisher = sockets.get(0); boolean rc = publisher.bind("tcp://*:" + port); assertThat(rc, is(true)); rc = poller.register(publisher, ZPoller.OUT); assertThat(rc, is(true)); } @Override public String premiere(Socket pipe) { wait.countDown(); return "Publisher"; } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { ZMQ.msleep(100); String string = String.format("%c-%05d", 'A' + rand.nextInt(10), ++count); return socket.send(string); } } // .split listener thread // The listener receives all messages flowing through the proxy, on its // pipe. In CZMQ, the pipe is a pair of ZMQ_PAIR sockets that connect // attached child threads. In other languages your mileage may vary: private static class Listener extends ZActor.SimpleActor { private final CountDownLatch wait; public Listener(CountDownLatch wait) { this.wait = wait; } @Override public List createSockets(ZContext ctx, Object... args) { Socket pull = ctx.createSocket(SocketType.PULL); assertThat(pull, notNullValue()); return Collections.singletonList(pull); } @Override public void start(Socket pipe, List sockets, ZPoller poller) { Socket subscriber = sockets.get(0); boolean rc = subscriber.connect("inproc://captured"); assertThat(rc, is(true)); rc = poller.register(subscriber, ZPoller.IN); assertThat(rc, is(true)); wait.countDown(); } @Override public String premiere(Socket pipe) { wait.countDown(); return "Listener"; } @Override public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events) { ZFrame frame = ZFrame.recvFrame(socket); assertThat(frame, notNullValue()); frame.print(null); frame.destroy(); return true; } } private static class Proxy extends ZProxy.Proxy.SimpleProxy { private final int frontend; private final int backend; public Proxy(int frontend, int backend) { this.frontend = frontend; this.backend = backend; } @Override public Socket create(ZContext ctx, Plug place, Object... args) { switch (place) { case FRONT: return ctx.createSocket(SocketType.XSUB); case BACK: return ctx.createSocket(SocketType.XPUB); case CAPTURE: return ctx.createSocket(SocketType.PUSH); default: return null; } } @Override public boolean configure(Socket socket, Plug place, Object... args) { switch (place) { case FRONT: return socket.connect("tcp://localhost:" + frontend); case BACK: return socket.bind("tcp://*:" + backend); case CAPTURE: return socket.bind("inproc://captured"); default: return true; } } } // .split main thread // The main task starts the subscriber and publisher, and then sets // itself up as a listening proxy. The listener runs as a child thread: @Test(timeout = 5000) public void testEspresso() throws IOException, InterruptedException { final int frontend = Utils.findOpenPort(); final int backend = Utils.findOpenPort(); final CountDownLatch wait = new CountDownLatch(3); try (final ZContext ctx = new ZContext()) { ZActor publisher = new ZActor(ctx, new Publisher(frontend, wait), "motdelafin"); ZActor subscriber = new ZActor(ctx, new Subscriber(backend, wait), "motdelafin"); ZActor listener = new ZActor(ctx, new Listener(wait), "motdelafin"); ZProxy proxy = ZProxy.newZProxy(ctx, "Proxy", new Proxy(frontend, backend), "motdelafin"); String status = proxy.start(true); assertThat(status, is(ZProxy.STARTED)); wait.await(); boolean rc = publisher.send("anything-sent-will-end-the-actor"); assertThat(rc, is(true)); // subscriber is already stopped after 5 receptions rc = listener.send("Did I really say ANYTHING?"); assertThat(rc, is(true)); status = proxy.exit(); assertThat(status, is(ZProxy.EXITED)); boolean rcsub = subscriber.send("anything-sent-will-end-the-actor"); assertThat(rcsub, is(true)); publisher.exit().await(); subscriber.exit().await(); listener.exit().await(); } } } jeromq-0.6.0/src/test/java/org/zeromq/proto/000077500000000000000000000000001455771126300207635ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/proto/ZNeedleTest.java000066400000000000000000000146331455771126300240230ustar00rootroot00000000000000package org.zeromq.proto; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.junit.Test; import org.zeromq.ZFrame; import org.zeromq.ZMQ; public class ZNeedleTest { @Test public void testGetByte() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); assertThat(needle.getNumber1(), is(0)); } @Test(expected = IllegalArgumentException.class) public void testGetIncorrectByte() { ZFrame frame = new ZFrame(new byte[0]); ZNeedle needle = new ZNeedle(frame); needle.getNumber1(); } @Test public void testGetShort() { ZFrame frame = new ZFrame(new byte[2]); ZNeedle needle = new ZNeedle(frame); assertThat(needle.getNumber2(), is(0)); } @Test(expected = IllegalArgumentException.class) public void testGetIncorrectShort() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); needle.getNumber2(); } @Test public void testGetInt() { ZFrame frame = new ZFrame(new byte[4]); ZNeedle needle = new ZNeedle(frame); assertThat(needle.getNumber4(), is(0)); } @Test(expected = IllegalArgumentException.class) public void testGetIncorrectInt() { ZFrame frame = new ZFrame(new byte[3]); ZNeedle needle = new ZNeedle(frame); needle.getNumber4(); } @Test public void testGetLong() { ZFrame frame = new ZFrame(new byte[8]); ZNeedle needle = new ZNeedle(frame); assertThat(needle.getNumber8(), is(0L)); } @Test(expected = IllegalArgumentException.class) public void testGetIncorrectLong() { ZFrame frame = new ZFrame(new byte[7]); ZNeedle needle = new ZNeedle(frame); needle.getNumber8(); } @Test public void testSimpleValues() { ZFrame frame = new ZFrame(new byte[15 + 26 + 1 + 4 + 8 + 2]); ZNeedle needle = new ZNeedle(frame); needle.putNumber1(1); needle.putNumber2(124); needle.putNumber4(3245678); needle.putNumber8(87654225); byte[] block = new byte[10]; Arrays.fill(block, (byte) 2); needle.putBlock(block, 8); needle.putShortString("abcdefg"); needle.putLongString("hijklmnopqrstuvwxyz"); needle.putNumber2(421); needle = new ZNeedle(frame); assertThat(needle.getNumber1(), is(1)); assertThat(needle.getNumber2(), is(124)); assertThat(needle.getNumber4(), is(3245678)); assertThat(needle.getNumber8(), is(87654225L)); block = new byte[8]; Arrays.fill(block, (byte) 2); assertThat(needle.getBlock(8), is(block)); assertThat(needle.getShortString(), is("abcdefg")); assertThat(needle.getLongString(), is("hijklmnopqrstuvwxyz")); assertThat(needle.getNumber2(), is(421)); String s = needle.toString(); assertThat(s, is("ZNeedle [position=56, ceiling=56]")); } @Test public void testLongString() { ZFrame frame = new ZFrame(new byte[300]); ZNeedle needle = new ZNeedle(frame); byte[] bytes = new byte[294]; Arrays.fill(bytes, (byte) 'A'); String string = new String(bytes, ZMQ.CHARSET); needle.putString(string); needle.putNumber2(42); needle = new ZNeedle(frame); assertThat(needle.getLongString(), is(string)); assertThat(needle.getNumber2(), is(42)); } @Test public void testList() { ZFrame frame = new ZFrame(new byte[26]); ZNeedle needle = new ZNeedle(frame); needle.putList(Arrays.asList("1", "2", "34", "567")); needle.putList(Arrays.asList("864", "43", "9", "0")); needle = new ZNeedle(frame); assertThat(needle.getList(), is(Arrays.asList("1", "2", "34", "567"))); assertThat(needle.getList(), is(Arrays.asList("864", "43", "9", "0"))); } @Test public void testNullList() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); needle.putList(null); needle = new ZNeedle(frame); assertThat(needle.getList(), is(Collections.emptyList())); } @Test public void testEmptyList() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); needle.putList(Collections.emptyList()); needle = new ZNeedle(frame); assertThat(needle.getList(), is(Collections.emptyList())); } @Test public void testMap() { ZFrame frame = new ZFrame(new byte[2 * (1 + 10 + 4 + 7)]); ZNeedle needle = new ZNeedle(frame); Map map = new HashMap<>(); map.put("key", "value"); map.put("1", "2"); map.put("34", "567"); needle.putMap(map); needle.putMap(map); needle = new ZNeedle(frame); assertThat(needle.getMap(), is(map)); assertThat(new HashSet<>(needle.getList()), is(new HashSet<>(Arrays.asList("key=value", "1=2", "34=567")))); } @Test public void testNullMap() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); needle.putMap(null); needle = new ZNeedle(frame); assertThat(needle.getMap(), is(Collections.emptyMap())); } @Test public void testEmptyMap() { ZFrame frame = new ZFrame(new byte[1]); ZNeedle needle = new ZNeedle(frame); needle.putMap(Collections.emptyMap()); needle = new ZNeedle(frame); assertThat(needle.getMap(), is(Collections.emptyMap())); } @Test(expected = IllegalArgumentException.class) public void testMapIncorrectKey() { ZFrame frame = new ZFrame(new byte[(1 + 10)]); ZNeedle needle = new ZNeedle(frame); Map map = new HashMap<>(); map.put("ke=", "value"); needle.putMap(map); } @Test(expected = IllegalArgumentException.class) public void testMapIncorrectValue() { ZFrame frame = new ZFrame(new byte[(1 + 10)]); ZNeedle needle = new ZNeedle(frame); Map map = new HashMap<>(); map.put("key", "=alue"); needle.putMap(map); } } jeromq-0.6.0/src/test/java/org/zeromq/proto/ZPictureTest.java000066400000000000000000000131141455771126300242330ustar00rootroot00000000000000package org.zeromq.proto; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import org.zeromq.SocketType; import org.zeromq.ZFrame; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Context; import org.zeromq.ZMQ.Socket; import org.zeromq.ZMQException; import org.zeromq.ZMsg; public class ZPictureTest { private final ZPicture pic = new ZPicture(); @Test(expected = ZMQException.class) public void testInvalidBinaryPictureFormat() { String picture = "a"; pic.msgBinaryPicture(picture, 255); } @Test(expected = ZMQException.class) public void testSendInvalidPictureFormat() { String picture = " "; pic.sendPicture(null, picture, 255); } @Test(expected = ZMQException.class) public void testReceiveInvalidPictureFormat() { String picture = "x"; pic.recvPicture(null, picture); } @Test(expected = ZMQException.class) public void testInvalidPictureMsgNotInTheEnd() { String picture = "m1"; ZMsg msg = new ZMsg().push("Hello"); pic.msgBinaryPicture(picture, msg, 255); } @Test(expected = ZMQException.class) public void testReceiveInvalidPictureMsgNotInTheEnd() { String picture = "m1"; pic.recvBinaryPicture(null, picture); } @Test public void testValidPictureNullMsgInTheEnd() { String picture = "fm"; ZMsg msg = pic.msgBinaryPicture(picture, new ZFrame("My frame"), null); assertThat(msg.getLast().size(), is(0)); } @Test public void testSocketSendRecvPicture() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); String picture = "i1248sbcfzm"; ZMsg msg = new ZMsg(); msg.add("Hello"); msg.add("World"); rc = pic.sendPicture( push, picture, -456, 255, 65535, 429496729, Long.MAX_VALUE, "Hello World", "ABC".getBytes(ZMQ.CHARSET), "DEF".getBytes(ZMQ.CHARSET), new ZFrame("My frame"), msg); assertThat(rc, is(true)); Object[] objects = pic.recvPicture(pull, picture); assertThat(objects[0], is(-456)); assertThat(objects[1], is(255)); assertThat(objects[2], is(65535)); assertThat(objects[3], is(429496729)); assertThat(objects[4], is(Long.MAX_VALUE)); assertThat(objects[5], is("Hello World")); assertThat(objects[6], is("ABC".getBytes(zmq.ZMQ.CHARSET))); assertThat(objects[7], is("DEF".getBytes(zmq.ZMQ.CHARSET))); assertThat(objects[8], is(equalTo(new ZFrame("My frame")))); assertThat(objects[9], is(equalTo(new ZFrame((byte[]) null)))); ZMsg expectedMsg = new ZMsg(); expectedMsg.add("Hello"); expectedMsg.add("World"); assertThat(objects[10], is(equalTo(expectedMsg))); push.close(); pull.close(); context.term(); } @Test public void testSocketSendRecvBinaryPicture() { Context context = ZMQ.context(1); Socket push = context.socket(SocketType.PUSH); Socket pull = context.socket(SocketType.PULL); boolean rc = pull.setReceiveTimeOut(50); assertThat(rc, is(true)); int port = push.bindToRandomPort("tcp://127.0.0.1"); rc = pull.connect("tcp://127.0.0.1:" + port); assertThat(rc, is(true)); String picture = "1248sSbcfm"; ZMsg msg = new ZMsg(); msg.add("Hello"); msg.add("World"); rc = pic.sendBinaryPicture( push, picture, 255, 65535, 429496729, Long.MAX_VALUE, "Hello World", "Hello cruel World!", "ABC".getBytes(ZMQ.CHARSET), "DEF".getBytes(ZMQ.CHARSET), new ZFrame("My frame"), msg); assertThat(rc, is(true)); Object[] objects = pic.recvBinaryPicture(pull, picture); assertThat(objects[0], is(255)); assertThat(objects[1], is(65535)); assertThat(objects[2], is(429496729)); assertThat(objects[3], is(Long.MAX_VALUE)); assertThat(objects[4], is("Hello World")); assertThat(objects[5], is("Hello cruel World!")); assertThat(objects[6], is("ABC".getBytes(zmq.ZMQ.CHARSET))); assertThat(objects[7], is("DEF".getBytes(zmq.ZMQ.CHARSET))); assertThat(objects[8], is(equalTo(new ZFrame("My frame")))); ZMsg expectedMsg = new ZMsg(); expectedMsg.add("Hello"); expectedMsg.add("World"); assertThat(objects[9], is(equalTo(expectedMsg))); push.close(); pull.close(); context.term(); } } jeromq-0.6.0/src/test/java/org/zeromq/timer/000077500000000000000000000000001455771126300207405ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/timer/ZTickerTest.java000066400000000000000000000076201455771126300240230ustar00rootroot00000000000000package org.zeromq.timer; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.Before; import org.junit.Test; import org.zeromq.timer.ZTicket.Ticket; import org.zeromq.timer.ZTimer.Timer; import zmq.ZMQ; public class ZTickerTest { private ZTicker ticker; private final TimerHandler handler = args -> { AtomicInteger invoked = (AtomicInteger) args[0]; invoked.incrementAndGet(); }; private static final TimerHandler NOOP = args -> { // do nothing }; @Before public void setup() { ticker = new ZTicker(); } @Test public void testNoTicket() { assertThat(ticker.timeout(), is(-1L)); } @Test(expected = IllegalArgumentException.class) public void testInvalidTicketInsertion() { ticker.addTicket(-1, NOOP); } @Test(expected = IllegalArgumentException.class) public void testInvalidTimerInsertion() { ticker.addTimer(-1, NOOP); } @Test public void testTicketInsertion() { ZTicker ticker = new ZTicker(() -> 0L); Ticket ticket = ticker.addTicket(1, NOOP); assertThat(ticket, is(notNullValue())); assertThat(ticker.timeout(), is(1L)); } @Test public void testTicketTimeout() { ZTicker ticker = new ZTicker(); Ticket ticket = ticker.addTicket(1, NOOP); ZMQ.sleep(1); assertThat(ticket, is(notNullValue())); assertThat(ticker.timeout(), is(0L)); } @Test public void testTimerInsertion() { ZTicker ticker = new ZTicker(() -> 0L); Timer timer = ticker.addTimer(1, NOOP); assertThat(timer, is(notNullValue())); assertThat(ticker.timeout(), is(1L)); } @Test public void testTimerAndTicketInsertion() { ZTicker ticker = new ZTicker(() -> 10L); Ticket ticket = ticker.addTicket(1, NOOP); assertThat(ticket, is(notNullValue())); Timer timer = ticker.addTimer(2, NOOP); assertThat(timer, is(notNullValue())); assertThat(ticker.timeout(), is(1L)); } @Test public void testExecution() { AtomicLong time = new AtomicLong(); ZTicker ticker = new ZTicker(time::get); AtomicInteger timerTriggered = new AtomicInteger(); AtomicInteger ticketTriggered = new AtomicInteger(); Timer timer = ticker.addTimer(10, handler, timerTriggered); assertThat(timer, is(notNullValue())); Ticket ticket = ticker.addTicket(30, handler, ticketTriggered); assertThat(ticket, is(notNullValue())); long timeout = ticker.timeout(); assertThat(timeout, is(10L)); time.set(time.get() + timeout); int executed = ticker.execute(); assertThat(executed, is(1)); assertThat(timerTriggered.get(), is(1)); assertThat(ticketTriggered.get(), is(0)); timeout = ticker.timeout(); assertThat(timeout, is(10L)); time.set(time.get() + timeout); executed = ticker.execute(); assertThat(executed, is(1)); assertThat(timerTriggered.get(), is(2)); assertThat(ticketTriggered.get(), is(0)); timeout = ticker.timeout(); assertThat(timeout, is(10L)); time.set(time.get() + timeout); executed = ticker.execute(); assertThat(executed, is(2)); assertThat(timerTriggered.get(), is(3)); assertThat(ticketTriggered.get(), is(1)); timeout = ticker.timeout(); assertThat(timeout, is(10L)); time.set(time.get() + timeout); executed = ticker.execute(); assertThat(executed, is(1)); assertThat(timerTriggered.get(), is(4)); assertThat(ticketTriggered.get(), is(1)); } } jeromq-0.6.0/src/test/java/org/zeromq/timer/ZTicketTest.java000066400000000000000000000231711455771126300240240ustar00rootroot00000000000000package org.zeromq.timer; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.Before; import org.junit.Test; import org.zeromq.timer.ZTicket.Ticket; public class ZTicketTest { final AtomicLong time = new AtomicLong(); private ZTicket tickets = new ZTicket(time::get); private AtomicInteger invoked = new AtomicInteger(); private final TimerHandler handler = args -> { AtomicInteger invoked = (AtomicInteger) args[0]; invoked.incrementAndGet(); }; private static final TimerHandler NOOP = args -> { // do nothing }; @Before public void setup() { time.set(0); invoked = new AtomicInteger(); } @Test public void testNoTicket() { assertThat(tickets.timeout(), is(-1L)); } @Test(expected = IllegalArgumentException.class) public void testInvalidInsertion() { tickets.add(-1, NOOP); } @Test public void testSingleInsertion() { tickets.add(1, NOOP); assertThat(tickets.timeout(), is(1L)); } @Test public void testMultipleInsertions() { tickets.add(2, NOOP); tickets.add(40, NOOP); tickets.add(30, NOOP); assertThat(tickets.timeout(), is(2L)); time.set(2); int rc = tickets.execute(); assertThat(rc, is(1)); assertThat(tickets.timeout(), is(28L)); time.set(29); rc = tickets.execute(); assertThat(rc, is(0)); time.set(30); rc = tickets.execute(); assertThat(rc, is(1)); assertThat(tickets.timeout(), is(10L)); } @Test public void testAddFaultyHandler() { ZTicket.Ticket ticket = tickets.add(10, null); assertThat(ticket, nullValue()); } @Test public void testCancelTwice() { ZTicket.Ticket ticket = tickets.add(10, handler); assertThat(ticket, notNullValue()); boolean rc = ticket.cancel(); assertThat(rc, is(true)); rc = ticket.cancel(); assertThat(rc, is(false)); } @Test public void testNotInvokedInitial() { tickets.add(100, handler, invoked); // Ticker should not have been invoked yet int rc = tickets.execute(); assertThat(rc, is(0)); } @Test public void testNotInvokedJustBefore() { tickets.add(100, handler, invoked); // Wait just before expiration and check again time.set(99); int rc = tickets.execute(); assertThat(rc, is(0)); } @Test public void testInvoked() { long fullTimeout = 100; tickets.add(fullTimeout, handler, invoked); // Wait until the end time.set(fullTimeout); int rc = tickets.execute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(1)); } @Test public void testInvoked2Times() { long fullTimeout = 100; tickets.add(fullTimeout, handler, invoked); tickets.add(fullTimeout, handler, invoked); // Wait until the end time.set(fullTimeout); int rc = tickets.execute(); assertThat(rc, is(2)); assertThat(invoked.get(), is(2)); } @Test public void testNotInvokedAfter() { long fullTimeout = 50; tickets.add(fullTimeout, handler, invoked); // Wait until the end time.set(fullTimeout); int rc = tickets.execute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(1)); // Wait half the time and check again time.set(time.get() + fullTimeout); rc = tickets.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(1)); } @Test public void testNotInvokedAfterResetHalfTime() { long fullTimeout = 50; ZTicket.Ticket ticket = tickets.add(fullTimeout, handler, invoked); // Wait half the time and check again time.set(fullTimeout / 2); int rc = tickets.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(0)); // Reset ticket and wait half of the time left ticket.reset(); time.set(time.get() + fullTimeout / 2); rc = tickets.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(0)); } @Test public void testInvokedAfterReset() { long fullTimeout = 50; testNotInvokedAfterResetHalfTime(); // Wait until the end time.set(time.get() + fullTimeout); int rc = tickets.execute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(1)); } @Test public void testReschedule() { long fullTimeout = 50; ZTicket.Ticket ticket = tickets.add(fullTimeout, handler, invoked); // reschedule ticket.setDelay(fullTimeout / 2); time.set(fullTimeout / 2); int rc = tickets.execute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(1)); } @Test public void testCancelledTimerIsRemoved() { ArrayList list = new ArrayList<>(); tickets = new ZTicket(time::get, list); ZTicket.Ticket ticket100 = tickets.add(100, handler, invoked); ZTicket.Ticket ticket1000 = tickets.add(1000, handler, invoked); ZTicket.Ticket ticket10 = tickets.add(10, handler, invoked); long timeout = tickets.timeout(); // timeout sorted the tickets, by order of execution assertThat(timeout, is(10L)); assertThat(list.get(0), is(ticket10)); assertThat(list.get(1), is(ticket100)); assertThat(list.get(2), is(ticket1000)); ticket10.cancel(); // cancel did not touch the order assertThat(list.get(0), is(ticket10)); assertThat(list.get(1), is(ticket100)); assertThat(list.get(2), is(ticket1000)); timeout = tickets.timeout(); // timeout resorted the tickets by order of execution assertThat(timeout, is(100L)); assertThat(list.get(0), is(ticket100)); assertThat(list.get(1), is(ticket1000)); assertThat(list.get(2), is(ticket10)); int rc = tickets.execute(); // execute deleted the cancelled tickets assertThat(rc, is(0)); assertThat(list.size(), is(2)); assertThat(list.get(0), is(ticket100)); assertThat(list.get(1), is(ticket1000)); ticket10 = tickets.add(10, handler, invoked); assertThat(list.get(0), is(ticket100)); assertThat(list.get(1), is(ticket1000)); assertThat(list.get(2), is(ticket10)); ticket10.cancel(); rc = tickets.execute(); // execute deleted the cancelled tickets assertThat(rc, is(0)); assertThat(list.size(), is(2)); assertThat(list.get(0), is(ticket100)); assertThat(list.get(1), is(ticket1000)); } @Test public void testCancel() { long fullTimeout = 50; ZTicket.Ticket ticket = tickets.add(fullTimeout, handler, invoked); // cancel ticket boolean ret = ticket.cancel(); assertThat(ret, is(true)); time.set(fullTimeout * 2); int rc = tickets.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(0)); } @Test public void testExtraLongInsertions() { int max = 100_000; Random random = new Random(); List delays = new ArrayList<>(); List tickets = new ArrayList<>(); for (int idx = 0; idx < max; ++idx) { delays.add(random.nextInt(1_000_000) + 1); } long start = System.currentTimeMillis(); for (int idx = 0; idx < max; ++idx) { tickets.add(this.tickets.add(delays.get(idx), NOOP)); } long end = System.currentTimeMillis(); long elapsed = end - start; System.out.printf("ZTicket Add: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); start = System.currentTimeMillis(); long timeout = this.tickets.timeout(); end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTicket Timeout: %s millisec %n", elapsed); this.time.set(this.time.get() + timeout); start = System.currentTimeMillis(); int rc = this.tickets.execute(); end = System.currentTimeMillis(); elapsed = end - start; assertThat(rc > 0, is(true)); System.out.printf("ZTicket Execute: %s millisec %n", elapsed); start = System.currentTimeMillis(); for (Ticket t : tickets) { t.reset(); } end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTicket Reset: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); start = System.currentTimeMillis(); for (Ticket t : tickets) { t.cancel(); } end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTicket Cancel: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); } } jeromq-0.6.0/src/test/java/org/zeromq/timer/ZTimerTest.java000066400000000000000000000151261455771126300236620ustar00rootroot00000000000000package org.zeromq.timer; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.junit.Before; import org.junit.Test; import org.zeromq.timer.ZTimer.Timer; public class ZTimerTest { private final AtomicLong time = new AtomicLong(); private final ZTimer timers = new ZTimer(time::get); private AtomicBoolean invoked = new AtomicBoolean(); private final TimerHandler handler = args -> { AtomicBoolean invoked = (AtomicBoolean) args[0]; invoked.set(true); }; private static final TimerHandler NOOP = args -> { // do nothing }; @Before public void setup() { time.set(0); invoked = new AtomicBoolean(); } @Test public void testAddFaultyHandler() { Timer timer = timers.add(10, null); assertThat(timer, nullValue()); } @Test public void testCancelTwice() { Timer timer = timers.add(10, handler); assertThat(timer, notNullValue()); boolean rc = timer.cancel(); assertThat(rc, is(true)); rc = timer.cancel(); assertThat(rc, is(false)); } @Test public void testTimeoutNoActiveTimers() { long timeout = timers.timeout(); assertThat(timeout, is(-1L)); } @Test public void testNotInvokedInitial() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Timer should not have been invoked yet int rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testNotInvokedHalfTime() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Wait half the time and check again long timeout = timers.timeout(); time.set(time.get() + timeout / 2); int rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testInvoked() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); time.set(time.get() + fullTimeout); // Wait until the end timers.sleepAndExecute(); assertThat(invoked.get(), is(true)); } @Test public void testNotInvokedAfterHalfTimeAgain() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); time.set(time.get() + fullTimeout); // Wait until the end timers.sleepAndExecute(); assertThat(invoked.get(), is(true)); // Wait half the time and check again long timeout = timers.timeout(); time.set(time.get() + timeout / 2); int rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testNotInvokedAfterResetHalfTime() { assertNotInvokedAfterResetHalfTime(); } private AtomicLong assertNotInvokedAfterResetHalfTime() { long fullTimeout = 100; Timer timer = timers.add(fullTimeout, handler, invoked); // Wait half the time and check again long timeout = timers.timeout(); time.set(time.get() + timeout / 2); int rc = timers.execute(); assertThat(rc, is(0)); // Reset timer and wait half of the time left boolean ret = timer.reset(); assertThat(ret, is(true)); time.set(time.get() + timeout / 2); rc = timers.execute(); assertThat(rc, is(0)); return time; } @Test public void testInvokedAfterReset() { assertNotInvokedAfterResetHalfTime(); time.set(time.get() + 100 / 2); timers.execute(); assertThat(invoked.get(), is(true)); } @Test public void testReschedule() { long fullTimeout = 100; Timer timer = timers.add(fullTimeout, handler, invoked); // reschedule boolean ret = timer.setInterval(50); assertThat(ret, is(true)); time.set(time.get() + fullTimeout / 2); timers.execute(); assertThat(invoked.get(), is(true)); } @Test public void testCancel() { long fullTimeout = 100; Timer timer = timers.add(fullTimeout, handler, invoked); // cancel timer long timeout = timers.timeout(); boolean ret = timer.cancel(); assertThat(ret, is(true)); time.set(time.get() + timeout * 2); int rc = timers.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(false)); } @Test public void testExtraLongInsertions() { int max = 100_000; Random random = new Random(); List delays = new ArrayList<>(); List timers = new ArrayList<>(); for (int idx = 0; idx < max; ++idx) { delays.add(random.nextInt(1_000_000) + 1); } long start = System.currentTimeMillis(); for (int idx = 0; idx < max; ++idx) { timers.add(this.timers.add(delays.get(idx), NOOP)); } long end = System.currentTimeMillis(); long elapsed = end - start; System.out.printf("ZTimer Add: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); start = System.currentTimeMillis(); long timeout = this.timers.timeout(); end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTimer Timeout: %s millisec %n", elapsed); this.time.set(this.time.get() + timeout); start = System.currentTimeMillis(); int rc = this.timers.execute(); end = System.currentTimeMillis(); elapsed = end - start; assertThat(rc > 0, is(true)); System.out.printf("ZTimer Execute: %s millisec %n", elapsed); start = System.currentTimeMillis(); for (Timer t : timers) { t.reset(); } end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTimer Reset: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); start = System.currentTimeMillis(); for (Timer t : timers) { t.cancel(); } end = System.currentTimeMillis(); elapsed = end - start; System.out.printf("ZTimer Cancel: %s millisec spent on %s iterations: %s microsecs%n", elapsed, max, 1000 * elapsed / ((double) max)); } } jeromq-0.6.0/src/test/java/org/zeromq/util/000077500000000000000000000000001455771126300205755ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/org/zeromq/util/ZDataTest.java000066400000000000000000000027271455771126300233130ustar00rootroot00000000000000package org.zeromq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Test; import org.zeromq.ZMQ; public class ZDataTest { @Test public void testPrint() { byte[] buf = new byte[10]; Arrays.fill(buf, (byte) 0xAA); ZData data = new ZData(buf); data.print(System.out, "ZData: "); } @Test public void testPrintNonPrintable() { byte[] buf = new byte[12]; Arrays.fill(buf, (byte) 0x04); ZData data = new ZData(buf); data.print(System.out, "ZData: "); } @Test public void testToString() { ZData data = new ZData("test".getBytes(ZMQ.CHARSET)); String string = data.toString(); assertThat(string, is("test")); } @Test public void testToStringNonPrintable() { byte[] buf = new byte[2]; Arrays.fill(buf, (byte) 0x04); ZData data = new ZData(buf); String string = data.toString(); assertThat(string, is("0404")); } @Test public void testStreq() { ZData data = new ZData("test".getBytes(ZMQ.CHARSET)); assertThat(data.streq("test"), is(true)); } @Test public void testEquals() { ZData data = new ZData("test".getBytes(ZMQ.CHARSET)); ZData other = new ZData("test".getBytes(ZMQ.CHARSET)); assertThat(data.equals(other), is(true)); } } jeromq-0.6.0/src/test/java/org/zeromq/util/ZDigestTest.java000066400000000000000000000023561455771126300236570ustar00rootroot00000000000000package org.zeromq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Test; public class ZDigestTest { @Test public void testData() { byte[] buf = new byte[1024]; Arrays.fill(buf, (byte) 0xAA); ZDigest digest = new ZDigest(); digest.update(buf); byte[] data = digest.data(); assertThat(byt(data[0]), is(0xDE)); assertThat(byt(data[1]), is(0xB2)); assertThat(byt(data[2]), is(0x38)); assertThat(byt(data[3]), is(0x07)); } @Test public void testSize() { byte[] buf = new byte[1024]; Arrays.fill(buf, (byte) 0xAA); ZDigest digest = new ZDigest(); digest.update(buf); int size = digest.size(); assertThat(size, is(20)); } private int byt(byte data) { return data & 0xff; } @Test public void testString() { byte[] buf = new byte[1024]; Arrays.fill(buf, (byte) 0xAA); ZDigest digest = new ZDigest(); digest.update(buf); String string = digest.string(); assertThat(string, is("DEB23807D4FE025E900FE9A9C7D8410C3DDE9671")); } } jeromq-0.6.0/src/test/java/perf/000077500000000000000000000000001455771126300164505ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/perf/InprocLat.java000066400000000000000000000067341455771126300212200ustar00rootroot00000000000000package perf; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class InprocLat { private InprocLat() { } static class Worker implements Runnable { private final Ctx ctx; private final int roundtripCount; Worker(Ctx ctx, int roundtripCount) { this.ctx = ctx; this.roundtripCount = roundtripCount; } @Override public void run() { SocketBase s = ZMQ.socket(ctx, ZMQ.ZMQ_REP); if (s == null) { printf("error in socket: %s\n"); exit(1); } boolean rc = ZMQ.connect(s, "inproc://lat_test"); if (!rc) { printf("error in connect: %s\n"); exit(1); } Msg msg; for (int i = 0; i != roundtripCount; i++) { msg = ZMQ.recvMsg(s, 0); if (msg == null) { printf("error in recvmsg: %s\n"); exit(1); } int r = ZMQ.sendMsg(s, msg, 0); if (r < 0) { printf("error in sendmsg: %s\n"); exit(1); } } ZMQ.close(s); } private void exit(int i) { // TODO Auto-generated method stub } } public static void main(String[] argv) throws Exception { if (argv.length != 2) { printf("usage: inproc_lat \n"); return; } int messageSize = atoi(argv[0]); int roundtripCount = atoi(argv[1]); Ctx ctx = ZMQ.init(1); if (ctx == null) { printf("error in init:"); return; } SocketBase s = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); if (s == null) { printf("error in socket: "); return; } boolean rc = ZMQ.bind(s, "inproc://lat_test"); if (!rc) { printf("error in bind: "); return; } Thread localThread = new Thread(new Worker(ctx, roundtripCount)); localThread.start(); Msg smsg = ZMQ.msgInitWithSize(messageSize); printf("message size: %d [B]\n", messageSize); printf("roundtrip count: %d\n", roundtripCount); long watch = ZMQ.startStopwatch(); for (int i = 0; i != roundtripCount; i++) { int r = ZMQ.sendMsg(s, smsg, 0); if (r < 0) { printf("error in sendmsg: %s\n"); return; } Msg msg = ZMQ.recvMsg(s, 0); if (msg == null) { printf("error in recvmsg: %s\n"); return; } if (ZMQ.msgSize(msg) != messageSize) { printf("message of incorrect size received\n"); return; } } long elapsed = ZMQ.stopStopwatch(watch); double latency = (double) elapsed / (roundtripCount * 2); localThread.join(); printf("average latency: %.3f [us]\n", latency); ZMQ.close(s); ZMQ.term(ctx); } private static int atoi(String string) { return Integer.parseInt(string); } private static void printf(String string) { System.out.println(string); } private static void printf(String string, Object... args) { System.out.printf((string) + "%n", args); } } jeromq-0.6.0/src/test/java/perf/LocalLat.java000066400000000000000000000040131455771126300210040ustar00rootroot00000000000000package perf; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class LocalLat { private LocalLat() { } public static void main(String[] args) { String bindTo; int roundtripCount; int messageSize; Ctx ctx; SocketBase s; boolean rc; int n; int i; Msg msg; if (args.length != 3) { printf("usage: local_lat " + "\n"); return; } bindTo = args[0]; messageSize = atoi(args[1]); roundtripCount = atoi(args[2]); ctx = ZMQ.init(1); if (ctx == null) { printf("error in init: %s\n"); return; } s = ZMQ.socket(ctx, ZMQ.ZMQ_REP); if (s == null) { printf("error in socket: %s\n", ZMQ.strerror(ctx.errno().get())); return; } rc = ZMQ.bind(s, bindTo); if (!rc) { printf("error in bind: %s\n", ZMQ.strerror(s.errno())); return; } for (i = 0; i != roundtripCount; i++) { msg = ZMQ.recvMsg(s, 0); if (msg == null) { printf("error in recvmsg: %s\n", ZMQ.strerror(s.errno())); return; } if (ZMQ.msgSize(msg) != messageSize) { printf("message of incorrect size received\n"); return; } n = ZMQ.sendMsg(s, msg, 0); if (n < 0) { printf("error in sendmsg: %s\n", ZMQ.strerror(s.errno())); return; } } ZMQ.sleep(1000); ZMQ.close(s); ZMQ.term(ctx); } private static int atoi(String string) { return Integer.parseInt(string); } private static void printf(String string) { System.out.println(string); } private static void printf(String string, Object... args) { System.out.printf((string) + "%n", args); } } jeromq-0.6.0/src/test/java/perf/LocalThr.java000066400000000000000000000054251455771126300210310ustar00rootroot00000000000000package perf; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class LocalThr { private LocalThr() { } public static void main(String[] argv) { String bindTo; long messageCount; int messageSize; Ctx ctx; SocketBase s; boolean rc; long i; Msg msg; long watch; long elapsed; long throughput; double megabits; if (argv.length != 3) { printf("usage: local_thr \n"); return; } bindTo = argv[0]; messageSize = atoi(argv[1]); messageCount = atol(argv[2]); ctx = ZMQ.init(1); if (ctx == null) { printf("error in init"); return; } s = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); if (s == null) { printf("error in socket"); } // Add your socket options here. // For example ZMQ_RATE, ZMQ_RECOVERY_IVL and ZMQ_MCAST_LOOP for PGM. rc = ZMQ.bind(s, bindTo); if (!rc) { printf("error in bind: %s\n"); return; } msg = ZMQ.recvMsg(s, 0); if (msg == null) { printf("error in recvmsg: %s\n"); return; } watch = ZMQ.startStopwatch(); for (i = 0; i != messageCount - 1; i++) { msg = ZMQ.recvMsg(s, 0); if (msg == null) { printf("error in recvmsg: %s\n"); return; } if (ZMQ.msgSize(msg) != messageSize) { printf("message of incorrect size received " + ZMQ.msgSize(msg)); return; } } elapsed = ZMQ.stopStopwatch(watch); if (elapsed == 0) { elapsed = 1; } throughput = (long) ((double) messageCount / (double) elapsed * 1000000L); megabits = (double) (throughput * messageSize * 8) / 1000000; printf("message elapsed: %.3f \n", (double) elapsed / 1000000L); printf("message size: %d [B]\n", messageSize); printf("message count: %d\n", (int) messageCount); printf("mean throughput: %d [msg/s]\n", (int) throughput); printf("mean throughput: %.3f [Mb/s]\n", megabits); ZMQ.close(s); ZMQ.term(ctx); } private static void printf(String str, Object... args) { // TODO Auto-generated method stub System.out.printf((str) + "%n", args); } private static int atoi(String string) { return Integer.parseInt(string); } private static long atol(String string) { return Long.parseLong(string); } private static void printf(String string) { System.out.println(string); } } jeromq-0.6.0/src/test/java/perf/RemoteThr.java000066400000000000000000000034731455771126300212330ustar00rootroot00000000000000package perf; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class RemoteThr { private RemoteThr() { } public static void main(String[] argv) { String connectTo; long messageCount; int messageSize; Ctx ctx; SocketBase s; boolean rc; long i; Msg msg; if (argv.length != 3) { printf("usage: remote_thr \n"); return; } connectTo = argv[0]; messageSize = atoi(argv[1]); messageCount = atol(argv[2]); ctx = ZMQ.init(1); if (ctx == null) { printf("error in init"); return; } s = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); if (s == null) { printf("error in socket"); } // Add your socket options here. // For example ZMQ_RATE, ZMQ_RECOVERY_IVL and ZMQ_MCAST_LOOP for PGM. rc = ZMQ.connect(s, connectTo); if (!rc) { printf("error in connect: %s\n"); return; } for (i = 0; i != messageCount; i++) { msg = ZMQ.msgInitWithSize(messageSize); if (msg == null) { printf("error in msg_init: %s\n"); return; } int n = ZMQ.sendMsg(s, msg, 0); if (n < 0) { printf("error in sendmsg: %s\n"); return; } } ZMQ.close(s); ZMQ.term(ctx); } private static int atoi(String string) { return Integer.parseInt(string); } private static long atol(String string) { return Long.parseLong(string); } private static void printf(String string) { System.out.println(string); } } jeromq-0.6.0/src/test/java/zmq/000077500000000000000000000000001455771126300163235ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/ConnectRidTest.java000066400000000000000000000175161455771126300220700ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class ConnectRidTest { @Test public void testStream2stream() { System.out.println("Test Stream 2 stream"); String host = "tcp://localhost:*"; Msg msg = new Msg("hi 1".getBytes(ZMQ.CHARSET)); Ctx ctx = ZMQ.init(1); assert (ctx != null); // Set up listener STREAM. SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assert (bind != null); ZMQ.setSocketOption(bind, ZMQ.ZMQ_CONNECT_RID, "connectRid"); ZMQ.setSocketOption(bind, ZMQ.ZMQ_LINGER, 0); boolean rc = ZMQ.bind(bind, host); assert (rc); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connection stream. SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assert (connect != null); ZMQ.setSocketOption(connect, ZMQ.ZMQ_LINGER, 0); // Do the connection. ZMQ.setSocketOption(connect, ZMQ.ZMQ_CONNECT_RID, "connectRid"); rc = ZMQ.connect(connect, host); assert (rc); ZMQ.sleep(1); /* Uncomment to test assert on duplicate rid. // Test duplicate connect attempt. ZMQ.setSocketOption(connect, ZMQ_CONNECT_RID, "conn1", 6); rc = ZMQ.connect(connect, host); assert (rc); */ // Send data to the bound stream. int ret = ZMQ.send(connect, "connectRid", ZMQ.ZMQ_SNDMORE); assert (10 == ret); ret = ZMQ.send(connect, msg, 0); assert (4 == ret); // Accept data on the bound stream. Msg recv = ZMQ.recv(bind, 0); assert (recv != null); recv = ZMQ.recv(bind, 0); assert (recv != null); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } @Test public void testRouter2routerNamed() { System.out.println("Test Router 2 Router named"); testRouter2router(true); } @Test public void testRouter2routerUnnamed() { System.out.println("Test Router 2 Router unnamed"); testRouter2router(false); } private void testRouter2router(boolean named) { String host = "tcp://localhost:*"; Msg msg = new Msg("hi 1".getBytes(ZMQ.CHARSET)); Ctx ctx = ZMQ.init(1); assert (ctx != null); // Set up listener STREAM. SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assert (bind != null); ZMQ.setSocketOption(bind, ZMQ.ZMQ_LINGER, 0); boolean rc = ZMQ.bind(bind, host); assert (rc); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connection stream. SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assert (connect != null); ZMQ.setSocketOption(connect, ZMQ.ZMQ_LINGER, 0); // Do the connection. ZMQ.setSocketOption(connect, ZMQ.ZMQ_CONNECT_RID, "connectRid"); rc = ZMQ.connect(connect, host); assert (rc); if (named) { ZMQ.setSocketOption(bind, ZMQ.ZMQ_IDENTITY, "X"); ZMQ.setSocketOption(connect, ZMQ.ZMQ_IDENTITY, "Y"); } ZMQ.sleep(1); /* Uncomment to test assert on duplicate rid. // Test duplicate connect attempt. ZMQ.setSocketOption(connect, ZMQ_CONNECT_RID, "conn1", 6); rc = ZMQ.connect(connect, host); assert (rc); */ // Send data to the bound stream. int ret = ZMQ.send(connect, "connectRid", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(10)); ret = ZMQ.send(connect, msg, 0); assertThat(ret, is(4)); Msg recv; // Receive the name. Msg name = ZMQ.recv(bind, 0); assertThat(name, notNullValue()); assert (name != null); if (named) { assertThat(name.data()[0], is((byte) 'Y')); } else { assertThat(name.data()[0], is((byte) 0)); } // Receive the data. recv = ZMQ.recv(bind, 0); assertThat(recv, notNullValue()); // Send some data back. if (named) { ret = ZMQ.send(bind, name, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); } else { ret = ZMQ.send(bind, name, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(5)); } ret = ZMQ.send(bind, "ok", 0); assertThat(ret, is(2)); // If bound socket identity naming a problem, we'll likely see something funky here. recv = ZMQ.recv(connect, 0); assertThat(recv, notNullValue()); assertThat(recv.data()[0], is((byte) 'c')); recv = ZMQ.recv(connect, 0); assertThat(recv, notNullValue()); assertThat(recv.data()[0], is((byte) 'o')); assertThat(recv.data()[1], is((byte) 'k')); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } @Test public void testRouter2routerWhileReceiving() { System.out.println("Test Router 2 router while receiving"); String wildcardAddress = "tcp://localhost:*"; Msg msg = new Msg("hi 1".getBytes(ZMQ.CHARSET)); Ctx ctx = ZMQ.init(1); assert (ctx != null); // Set up the router which both binds and connects SocketBase x = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assert (x != null); ZMQ.setSocketOption(x, ZMQ.ZMQ_LINGER, 0); ZMQ.setSocketOption(x, ZMQ.ZMQ_IDENTITY, "X"); boolean rc = ZMQ.bind(x, wildcardAddress); assert (rc); String xaddress = (String) ZMQ.getSocketOptionExt(x, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(xaddress, notNullValue()); // Set up the router which binds SocketBase z = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assert (z != null); ZMQ.setSocketOption(z, ZMQ.ZMQ_LINGER, 0); ZMQ.setSocketOption(z, ZMQ.ZMQ_IDENTITY, "Z"); rc = ZMQ.bind(z, wildcardAddress); assert (rc); String zaddress = (String) ZMQ.getSocketOptionExt(z, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(zaddress, notNullValue()); // Set up connect-only router. SocketBase y = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assert (y != null); ZMQ.setSocketOption(y, ZMQ.ZMQ_LINGER, 0); ZMQ.setSocketOption(y, ZMQ.ZMQ_IDENTITY, "Y"); // Do the connection. rc = ZMQ.connect(y, xaddress); assert (rc); // Send data from Y to X int ret = ZMQ.send(y, "X", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); ret = ZMQ.send(y, msg, 0); assertThat(ret, is(4)); // wait for the messages to be processed on the io thread ZMQ.msleep(100); // try to connect X to Z ZMQ.setSocketOption(x, ZMQ.ZMQ_CONNECT_RID, "Z"); rc = ZMQ.connect(x, zaddress); assert (rc); // Send data from X to Z ret = ZMQ.send(x, "Z", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); ret = ZMQ.send(x, msg, 0); assertThat(ret, is(4)); // wait for the messages to be delivered ZMQ.msleep(100); // Make sure that Y has not received any messages Msg name = ZMQ.recv(y, ZMQ.ZMQ_DONTWAIT); assertThat(name, nullValue()); // Receive the message from Z name = ZMQ.recv(z, 0); assertThat(name, notNullValue()); assertThat(name.data()[0], is((byte) 'X')); Msg recv; recv = ZMQ.recv(z, 0); assertThat(recv, notNullValue()); assertThat(recv.data()[0], is((byte) 'h')); ZMQ.close(x); ZMQ.close(y); ZMQ.close(z); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/CtxTest.java000066400000000000000000000027041455771126300205670ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Test; import zmq.socket.Sockets; public class CtxTest { @Test public void testSeveralPendingInprocSocketsAreClosedIssue595() { Ctx ctx = ZMQ.init(0); assertThat(ctx, notNullValue()); List sockets = new ArrayList<>(); for (Sockets type : Sockets.values()) { for (int idx = 0; idx < 3; ++idx) { SocketBase socket = ZMQ.socket(ctx, type.ordinal()); assertThat(socket, notNullValue()); boolean rc = socket.connect("inproc://" + type.name()); assertThat(rc, is(true)); sockets.add(socket); } } for (SocketBase socket : sockets) { ZMQ.close(socket); } ZMQ.term(ctx); assertThat(ctx.checkTag(), is(false)); } @Test public void testSetHandler() { Ctx ctx = ZMQ.init(0); SocketBase socket = ZMQ.socket(ctx, Sockets.CLIENT.ordinal()); Assert.assertThrows(IllegalStateException.class, () -> ctx.setNotificationExceptionHandler(null)); Assert.assertThrows(IllegalStateException.class, () -> ctx.setUncaughtExceptionHandler(null)); ZMQ.close(socket); } } jeromq-0.6.0/src/test/java/zmq/DiffServerTest.java000066400000000000000000000025001455771126300220620ustar00rootroot00000000000000package zmq; import java.io.IOException; import org.junit.Assert; import org.junit.Test; import zmq.util.Utils; public class DiffServerTest { @Test public void test() throws IOException { int port = Utils.findOpenPort(); String host = "tcp://localhost:" + port; int tos = 0x28; Ctx ctx = ZMQ.init(1); assert (ctx != null); SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assert (bind != null); ZMQ.setSocketOption(bind, ZMQ.ZMQ_TOS, tos); boolean rc = ZMQ.bind(bind, host); assert (rc); int option = ZMQ.getSocketOption(bind, ZMQ.ZMQ_TOS); Assert.assertEquals(tos, option); SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assert (connect != null); tos = 0x58; ZMQ.setSocketOption(connect, ZMQ.ZMQ_TOS, tos); rc = ZMQ.connect(connect, host); assert (rc); option = ZMQ.getSocketOption(connect, ZMQ.ZMQ_TOS); Assert.assertEquals(tos, option); // Wireshark can be used to verify that the server socket is // using DSCP 0x28 in packets to the client while the client // is using 0x58 in packets to the server. Helper.bounce(bind, connect); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/HeartbeatsTest.java000066400000000000000000000466021455771126300221200ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.Test; import zmq.util.AndroidProblematic; import zmq.util.TestUtils; import zmq.util.function.BiFunction; public class HeartbeatsTest { private static final long MAX = 100_000; // Tests sequentiality of received messages while heartbeating @Test @AndroidProblematic public void testSequentialityReceivedMessagesMultiThreadedPushBindPullConnect() throws IOException, InterruptedException { testSequentialityReceivedMessagesMultiThreaded(SocketBase::bind, SocketBase::connect); } // Tests sequentiality of received messages while heartbeating @Test @AndroidProblematic public void testSequentialityReceivedMessagesMultiThreadedPushConnectPullBind() throws IOException, InterruptedException { testSequentialityReceivedMessagesMultiThreaded(SocketBase::connect, SocketBase::bind); } private void testSequentialityReceivedMessagesMultiThreaded(BiFunction endpointPush, BiFunction endpointPull) throws IOException, InterruptedException { final int port = Utils.findOpenPort(); final String host = "localhost"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); final SocketBase push = ctx.createSocket(ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.setSocketOption(push, ZMQ.ZMQ_HEARTBEAT_IVL, 200); assertThat(rc, is(true)); rc = endpointPush.apply(push, String.format("tcp://%s:%s", host, port)); assertThat(rc, is(true)); final SocketBase pull = ctx.createSocket(ZMQ.ZMQ_PULL); rc = endpointPull.apply(pull, String.format("tcp://%s:%s", host, port)); assertThat(rc, is(true)); ExecutorService service = Executors.newFixedThreadPool(2); service.submit(() -> { Thread.currentThread().setName("Push"); long counter = 0; while (++counter < MAX) { String data = Long.toString(counter); int sent = ZMQ.send(push, Long.toString(counter), 0); assertThat(sent, is(data.length())); } System.out.println("Push finished"); push.close(); }); service.submit(() -> { Thread.currentThread().setName("Pull"); long counter = 0; while (++counter < MAX) { Msg msg = ZMQ.recv(pull, 0); assertThat(msg, notNullValue()); long received = Long.parseLong(new String(msg.data(), ZMQ.CHARSET)); assertThat(received, is(counter)); if (counter % (MAX / 10) == 0) { System.out.print(counter + " "); } } System.out.println("Pull finished"); pull.close(); }); service.shutdown(); service.awaitTermination(100, TimeUnit.SECONDS); ctx.terminate(); } // Tests sequentiality of received messages while heartbeating @Test public void testSequentialityReceivedMessagesSingleThread() throws IOException { final int port = Utils.findOpenPort(); final String host = "localhost"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ctx.createSocket(ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.setSocketOption(push, ZMQ.ZMQ_HEARTBEAT_IVL, 200); assertThat(rc, is(true)); rc = push.connect(String.format("tcp://%s:%s", host, port)); assertThat(rc, is(true)); SocketBase pull = ctx.createSocket(ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); rc = pull.bind(String.format("tcp://%s:%s", host, port)); assertThat(rc, is(true)); long counter = 0; while (++counter < MAX / 10) { String data = Long.toString(counter); int sent = ZMQ.send(push, Long.toString(counter), 0); assertThat(sent, is(data.length())); Msg msg = ZMQ.recv(pull, 0); assertThat(msg, notNullValue()); long received = Long.parseLong(new String(msg.data(), ZMQ.CHARSET)); assertThat(received, is(counter)); if (counter % (MAX / 100) == 0) { System.out.print(counter + " "); } } System.out.println(); push.close(); pull.close(); ctx.terminate(); } // this checks for heartbeat in REQ socket. @Test public void testHeartbeatReq() { final int heartbeatInterval = 100; String addr = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase req = ctx.createSocket(ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); boolean rc = req.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_IVL, heartbeatInterval); assertThat(rc, is(true)); SocketBase rep = ctx.createSocket(ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); rc = rep.bind(addr); assertThat(rc, is(true)); addr = (String) ZMQ.getSocketOptionExt(rep, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(addr, notNullValue()); rc = req.connect(addr); assertThat(rc, is(true)); final long start = System.currentTimeMillis(); do { // request int sent = ZMQ.send(req, "hello", 0); assertThat(sent, is("hello".length())); Msg msg = ZMQ.recv(rep, 0); assertThat(msg, notNullValue()); // reply sent = ZMQ.send(rep, "world", 0); assertThat(sent, is("world".length())); msg = ZMQ.recv(req, 0); assertThat(msg, notNullValue()); // let some time pass so several heartbeats are sent } while (System.currentTimeMillis() - start < 5 * heartbeatInterval); rep.close(); req.close(); ctx.terminate(); } // This checks for a broken TCP connection (or, in this case a stuck one // where the peer never responds to PINGS). There should be an accepted event // then a disconnect event. @Test public void testHeartbeatTimeout() throws IOException { testHeartbeatTimeout(false); } @Test public void testHeartbeatTimeoutWithContext() throws IOException { testHeartbeatTimeout(true); } private void testHeartbeatTimeout(boolean mockPing) throws IOException { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase server = prepServerSocket(ctx, !mockPing, false); assertThat(server, notNullValue()); SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); boolean rc = monitor.connect("inproc://monitor"); assertThat(rc, is(true)); String endpoint = (String) ZMQ.getSocketOptionExt(server, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); Socket socket = new Socket("127.0.0.1", TestUtils.port(endpoint)); // Mock a ZMTP 3 client so we can forcibly time out a connection mockHandshake(socket); if (mockPing) { mockPing(socket); } // By now everything should report as connected ZMQ.Event event = ZMQ.Event.read(monitor); assertThat(event.event, is(ZMQ.ZMQ_EVENT_ACCEPTED)); if (!mockPing) { // We should have been disconnected event = ZMQ.Event.read(monitor); assertThat(event.event, is(ZMQ.ZMQ_EVENT_DISCONNECTED)); } socket.close(); ZMQ.close(monitor); ZMQ.close(server); ZMQ.term(ctx); } // This checks that peers respect the TTL value in ping messages // We set up a mock ZMTP 3 client and send a ping message with a TTL // to a server that is not doing any heartbeating. Then we sleep, // if the server disconnects the client, then we know the TTL did // its thing correctly. @Test public void testHeartbeatTtl() { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase server = prepServerSocket(ctx, false, false); assertThat(server, notNullValue()); SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); boolean rc = monitor.connect("inproc://monitor"); assertThat(rc, is(true)); SocketBase client = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(client, notNullValue()); // Set the heartbeat TTL to 0.1 seconds rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HEARTBEAT_TTL, 100); assertThat(rc, is(true)); // Set the heartbeat interval to much longer than the TTL so that // the socket times out on the remote side. rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HEARTBEAT_IVL, 250); assertThat(rc, is(true)); String endpoint = (String) ZMQ.getSocketOptionExt(server, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); rc = ZMQ.connect(client, endpoint); assertThat(rc, is(true)); // By now everything should report as connected ZMQ.Event event = ZMQ.Event.read(monitor); assertThat(event.event, is(ZMQ.ZMQ_EVENT_ACCEPTED)); ZMQ.msleep(100); // We should have been disconnected event = ZMQ.Event.read(monitor); assertThat(event.event, is(ZMQ.ZMQ_EVENT_DISCONNECTED)); ZMQ.close(monitor); ZMQ.close(server); ZMQ.close(client); ZMQ.term(ctx); } // This checks for normal operation - that is pings and pongs being // exchanged normally. There should be an accepted event on the server, // and then no event afterwards. @Test public void testHeartbeatNoTimeoutWithCurve() { testHeartbeatNoTimeout(true, new byte[0]); } @Test public void testHeartbeatNoTimeoutWithoutCurve() { testHeartbeatNoTimeout(false, new byte[0]); } @Test public void testHeartbeatNoTimeoutWithoutCurveWithPingContext() { testHeartbeatNoTimeout(false, "context".getBytes(ZMQ.CHARSET)); } private void testHeartbeatNoTimeout(boolean curve, byte[] context) { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase server = prepServerSocket(ctx, true, context, curve); assertThat(server, notNullValue()); SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); boolean rc = monitor.connect("inproc://monitor"); assertThat(rc, is(true)); SocketBase client = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(client, notNullValue()); if (curve) { setupCurve(client, false); } String endpoint = (String) ZMQ.getSocketOptionExt(server, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); rc = ZMQ.connect(client, endpoint); assertThat(rc, is(true)); ZMQ.msleep(400); // By now everything should report as connected ZMQ.Event event = ZMQ.Event.read(monitor); assertThat(event.event, is(ZMQ.ZMQ_EVENT_ACCEPTED)); // We should still be connected because pings and pongs are happenin' event = ZMQ.Event.read(monitor, ZMQ.ZMQ_DONTWAIT); assertThat(event, nullValue()); ZMQ.close(monitor); ZMQ.close(server); ZMQ.close(client); ZMQ.term(ctx); } private SocketBase prepServerSocket(Ctx ctx, boolean heartBeats, boolean curve) { return prepServerSocket(ctx, heartBeats, new byte[0], curve); } private SocketBase prepServerSocket(Ctx ctx, boolean heartBeats, byte[] pingContext, boolean curve) { SocketBase server = ctx.createSocket(ZMQ.ZMQ_ROUTER); assertThat(server, notNullValue()); boolean rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); if (heartBeats) { rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_HEARTBEAT_IVL, 200); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_HEARTBEAT_CONTEXT, pingContext); assertThat(rc, is(true)); } if (curve) { setupCurve(server, true); } rc = ZMQ.bind(server, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); rc = ZMQ.monitorSocket( server, "inproc://monitor", ZMQ.ZMQ_EVENT_CONNECTED | ZMQ.ZMQ_EVENT_DISCONNECTED | ZMQ.ZMQ_EVENT_ACCEPTED); assertThat(rc, is(true)); return server; } private void setupCurve(SocketBase socket, boolean server) { String secretKey; String publicKey = "Yne@$w-vo bufsize) { src.get(buf); return bufsize; } src.get(buf, 0, remaining); return remaining; } } public static final DummyCtx ctx = new DummyCtx(); public static class DummyIOThread extends IOThread { public DummyIOThread() { super(ctx, 2); } } public static class DummySocket extends SocketBase { public DummySocket() { super(ctx, counter.get(), counter.get()); counter.incrementAndGet(); } @Override protected void xattachPipe(Pipe pipe, boolean icanhasall, boolean isLocallyInitiated) { } @Override protected void xpipeTerminated(Pipe pipe) { } } public static class DummySession extends SessionBase { public final List out = new ArrayList<>(); public DummySession() { this(new DummyIOThread(), false, new DummySocket(), new Options(), new Address(NetProtocol.tcp, "localhost:9090")); } public DummySession(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr) { super(ioThread, connect, socket, options, addr); } @Override public boolean pushMsg(Msg msg) { System.out.println("session.write " + msg); out.add(msg); return true; } @Override public Msg pullMsg() { System.out.println("session.read " + out); if (out.isEmpty()) { return null; } return out.remove(0); } } public static void bounce(SocketBase sb, SocketBase sc) { byte[] content = "12345678ABCDEFGH12345678abcdefgh".getBytes(ZMQ.CHARSET); // Send the message. int rc = ZMQ.send(sc, content, 32, ZMQ.ZMQ_SNDMORE); assert (rc == 32); rc = ZMQ.send(sc, content, 32, 0); assertThat(rc, is(32)); // Bounce the message back. Msg msg; msg = ZMQ.recv(sb, 0); assert (msg.size() == 32); long rcvmore = ZMQ.getSocketOption(sb, ZMQ.ZMQ_RCVMORE); assert (rcvmore == 1); msg = ZMQ.recv(sb, 0); assert (rc == 32); rcvmore = ZMQ.getSocketOption(sb, ZMQ.ZMQ_RCVMORE); assert (rcvmore == 0); rc = ZMQ.send(sb, new Msg(msg), ZMQ.ZMQ_SNDMORE); assert (rc == 32); rc = ZMQ.send(sb, new Msg(msg), 0); assert (rc == 32); // Receive the bounced message. msg = ZMQ.recv(sc, 0); assert (rc == 32); rcvmore = ZMQ.getSocketOption(sc, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(1L)); msg = ZMQ.recv(sc, 0); assert (rc == 32); rcvmore = ZMQ.getSocketOption(sc, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(0L)); // Check whether the message is still the same. //assert (memcmp (buf2, content, 32) == 0); } public static void expectBounceFail(SocketBase server, SocketBase client) { final byte[] content = "12345678ABCDEFGH12345678abcdefgh".getBytes(ZMQ.CHARSET); final int timeout = 250; final Errno errno = new Errno(); // Send message from client to server ZMQ.setSocketOption(client, ZMQ.ZMQ_SNDTIMEO, timeout); int rc = ZMQ.send(client, content, 32, ZMQ.ZMQ_SNDMORE); assert ((rc == 32) || ((rc == -1) && errno.is(ZError.EAGAIN))); rc = ZMQ.send(client, content, 32, 0); assert ((rc == 32) || ((rc == -1) && errno.is(ZError.EAGAIN))); // Receive message at server side (should not succeed) ZMQ.setSocketOption(server, ZMQ.ZMQ_RCVTIMEO, timeout); Msg msg = ZMQ.recv(server, 0); assert (msg == null); assert errno.is(ZError.EAGAIN); // Send message from server to client to test other direction // If connection failed, send may block, without a timeout ZMQ.setSocketOption(server, ZMQ.ZMQ_SNDTIMEO, timeout); rc = ZMQ.send(server, content, 32, ZMQ.ZMQ_SNDMORE); assert (rc == 32 || ((rc == -1) && errno.is(ZError.EAGAIN))); rc = ZMQ.send(server, content, 32, 0); assert (rc == 32 || ((rc == -1) && errno.is(ZError.EAGAIN))); // Receive message at client side (should not succeed) ZMQ.setSocketOption(client, ZMQ.ZMQ_RCVTIMEO, timeout); msg = ZMQ.recv(client, 0); assert (msg == null); assert errno.is(ZError.EAGAIN); } public static int send(SocketBase socket, String data) { return ZMQ.send(socket, data, 0); } public static int sendMore(SocketBase socket, String data) { return ZMQ.send(socket, data, ZMQ.ZMQ_SNDMORE); } public static String recv(SocketBase socket) { Msg msg = ZMQ.recv(socket, 0); assert (msg != null); return new String(msg.data(), ZMQ.CHARSET); } // Sends a message composed of frames that are C strings or null frames. // The list must be terminated by SEQ_END. // Example: s_send_seq (req, "ABC", 0, "DEF", SEQ_END); public static void sendSeq(SocketBase socket, String... data) { int rc; for (int idx = 0; idx < data.length - 1; ++idx) { rc = sendMore(socket, data[idx]); assert (rc == data[idx].length()); } rc = send(socket, data[data.length - 1]); assert (rc == data[data.length - 1].length()); } // Receives message a number of frames long and checks that the frames have // the given data which can be either C strings or 0 for a null frame. // The list must be terminated by SEQ_END. // Example: s_recv_seq (rep, "ABC", 0, "DEF", SEQ_END); public static void recvSeq(SocketBase socket, String... data) { String rc; for (String datum : data) { rc = recv(socket); assert (datum.equals(rc)); } } public static void send(Socket sa, String data) throws IOException { byte[] content = data.getBytes(ZMQ.CHARSET); byte[] length = String.format("%04d", content.length).getBytes(ZMQ.CHARSET); byte[] buf = new byte[1024]; int reslen; int rc; // Bounce the message back. InputStream in = sa.getInputStream(); OutputStream out = sa.getOutputStream(); out.write(length); out.write(content); System.out.println("sent " + data.length() + " " + data); int toRead = 4; // 4 + greeting_size int read = 0; while (toRead > 0) { rc = in.read(buf, read, toRead); read += rc; toRead -= rc; System.out.println("read " + rc + " total_read " + read + " toRead " + toRead); } System.out.printf("%02x %02x %02x %02x%n", buf[0], buf[1], buf[2], buf[3]); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } reslen = Integer.parseInt(new String(buf, 0, 4, ZMQ.CHARSET)); in.read(buf, 0, reslen); System.out.println("recv " + reslen + " " + new String(buf, 0, reslen, ZMQ.CHARSET)); } } jeromq-0.6.0/src/test/java/zmq/ImmediateTest.java000066400000000000000000000174741455771126300217410ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.util.Utils; public class ImmediateTest { @Test public void testImmediateTrue() throws Exception { System.out.println("Immediate = true"); // TEST 1. // First we're going to attempt to send messages to two // pipes, one connected, the other not. We should see // the PUSH load balancing to both pipes, and hence half // of the messages getting queued, as connect() creates a // pipe immediately. int pushPort1 = Utils.findOpenPort(); int pushPort2 = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); SocketBase to = ZMQ.socket(context, ZMQ.ZMQ_PULL); assertThat(to, notNullValue()); int val = 0; boolean rc = ZMQ.setSocketOption(to, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); rc = ZMQ.bind(to, "tcp://*:" + pushPort1); assertThat(rc, is(true)); // Create a socket pushing to two endpoints - only 1 message should arrive. SocketBase from = ZMQ.socket(context, ZMQ.ZMQ_PUSH); assertThat(from, notNullValue()); rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_IMMEDIATE, true); assertThat(rc, is(true)); val = 0; rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // This pipe will not connect rc = ZMQ.connect(from, "tcp://localhost:" + pushPort2); assertThat(rc, is(true)); // This pipe will rc = ZMQ.connect(from, "tcp://localhost:" + pushPort1); assertThat(rc, is(true)); // We send 10 messages, 5 should just get stuck in the queue // for the not-yet-connected pipe for (int i = 0; i < 10; ++i) { String message = "message "; message += ('0' + i); int sent = ZMQ.send(from, message, 0); assertThat(sent >= 0, is(true)); } ZMQ.sleep(1); // We now consume from the connected pipe // - we should see just 5 int timeout = 250; ZMQ.setSocketOption(to, ZMQ.ZMQ_RCVTIMEO, timeout); int seen = 0; for (int i = 0; i < 10; ++i) { Msg msg = ZMQ.recv(to, 0); if (msg == null) { break; // Break when we didn't get a message } seen++; } assertThat(seen, is(5)); ZMQ.close(from); ZMQ.close(to); ZMQ.term(context); } @Test public void testImmediateFalse() throws Exception { System.out.println("Immediate = false"); // TEST 2 // This time we will do the same thing, connect two pipes, // one of which will succeed in connecting to a bound // receiver, the other of which will fail. However, we will // also set the delay attach on connect flag, which should // cause the pipe attachment to be delayed until the connection // succeeds. int validPort = Utils.findOpenPort(); int invalidPort = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); SocketBase to = ZMQ.socket(context, ZMQ.ZMQ_PULL); assertThat(to, notNullValue()); boolean rc = ZMQ.bind(to, "tcp://*:" + validPort); assertThat(rc, is(true)); int val = 0; rc = ZMQ.setSocketOption(to, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // Create a socket pushing to two endpoints - all messages should arrive. SocketBase from = ZMQ.socket(context, ZMQ.ZMQ_PUSH); assertThat(from, notNullValue()); val = 0; rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // Set the key flag rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_IMMEDIATE, false); assertThat(rc, is(true)); // Connect to the invalid socket rc = ZMQ.connect(from, "tcp://localhost:" + invalidPort); assertThat(rc, is(true)); // Connect to the valid socket rc = ZMQ.connect(from, "tcp://localhost:" + validPort); assertThat(rc, is(true)); for (int i = 0; i < 10; ++i) { String message = "message "; message += ('0' + i); int sent = ZMQ.send(from, message, 0); assertThat(sent, is(message.length())); } int timeout = 250; ZMQ.setSocketOption(to, ZMQ.ZMQ_RCVTIMEO, timeout); int seen = 0; for (int i = 0; i < 10; ++i) { Msg msg = ZMQ.recv(to, 0); if (msg == null) { break; } seen++; } assertThat(seen, is(10)); ZMQ.close(from); ZMQ.close(to); ZMQ.term(context); } @Test(timeout = 5000) public void testImmediateFalseWithBrokenConnection() throws Exception { System.out.print("Immediate = false with broken connection"); // TEST 3 // This time we want to validate that the same blocking behaviour // occurs with an existing connection that is broken. We will send // messages to a connected pipe, disconnect and verify the messages // block. Then we reconnect and verify messages flow again. int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); SocketBase backend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(backend, notNullValue()); SocketBase frontend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(frontend, notNullValue()); final int linger = 0; ZMQ.setSocketOption(backend, ZMQ.ZMQ_LINGER, linger); ZMQ.setSocketOption(frontend, ZMQ.ZMQ_LINGER, linger); // Frontend connects to backend using IMMEDIATE ZMQ.setSocketOption(frontend, ZMQ.ZMQ_IMMEDIATE, false); boolean rc = ZMQ.bind(backend, "tcp://*:" + port); assertThat(rc, is(true)); rc = ZMQ.connect(frontend, "tcp://localhost:" + port); assertThat(rc, is(true)); System.out.print("."); // Ping backend to frontend so we know when the connection is up int sent = ZMQ.send(backend, "Hello", 0); assertThat(sent, is(5)); System.out.print("Ping"); Msg msg = ZMQ.recv(frontend, 0); System.out.print("."); assertThat(msg.size(), is(5)); // Send message from frontend to backend sent = ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT); assertThat(sent, is(5)); System.out.print("Message sent"); ZMQ.close(backend); System.out.print("."); System.out.print("Message send fail"); // Send a message, should fail // There's no way to do this except with a sleep and a loop while (ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Recreate backend socket backend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); ZMQ.setSocketOption(backend, ZMQ.ZMQ_LINGER, linger); rc = ZMQ.bind(backend, "tcp://*:" + port); assertThat(rc, is(true)); System.out.print("."); // Ping backend to frontend so we know when the connection is up sent = ZMQ.send(backend, "Hello", 0); assertThat(sent, is(5)); System.out.print("Ping"); msg = ZMQ.recv(frontend, 0); System.out.print("."); assertThat(msg.size(), is(5)); System.out.print("Message sent"); // After the reconnect, should succeed sent = ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT); assertThat(sent, is(5)); System.out.print("."); ZMQ.close(backend); ZMQ.close(frontend); ZMQ.term(context); System.out.println("Done"); } } jeromq-0.6.0/src/test/java/zmq/InprocDisconnectTest.java000066400000000000000000000071361455771126300233010ustar00rootroot00000000000000package zmq; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.nio.channels.Selector; import org.junit.Test; import zmq.poll.PollItem; public class InprocDisconnectTest { @Test public void testDisconnectInproc() { Ctx context = ZMQ.createContext(); Selector selector = context.createSelector(); try { testDisconnectInproc(context, selector); } finally { context.closeSelector(selector); } ZMQ.term(context); } private void testDisconnectInproc(Ctx context, Selector selector) { int publicationsReceived = 0; boolean isSubscribed = false; SocketBase pubSocket = ZMQ.socket(context, ZMQ.ZMQ_XPUB); SocketBase subSocket = ZMQ.socket(context, ZMQ.ZMQ_SUB); ZMQ.setSocketOption(subSocket, ZMQ.ZMQ_SUBSCRIBE, "foo".getBytes()); ZMQ.bind(pubSocket, "inproc://someInProcDescriptor"); int more; int iteration = 0; while (true) { PollItem[] items = { new PollItem(subSocket, ZMQ.ZMQ_POLLIN), // read publications new PollItem(pubSocket, ZMQ.ZMQ_POLLIN) // read subscriptions }; int rc = ZMQ.poll(selector, items, 2, 100L); if (items[1].isReadable()) { while (true) { Msg msg = ZMQ.recv(pubSocket, 0); int msgSize = msg.size(); byte[] buffer = msg.data(); if (buffer[0] == 0) { assertTrue(isSubscribed); System.out.printf("unsubscribing from '%s'\n", new String(buffer, 1, msgSize - 1)); isSubscribed = false; } else { assert (!isSubscribed); System.out.printf("subscribing on '%s'\n", new String(buffer, 1, msgSize - 1)); isSubscribed = true; } more = ZMQ.getSocketOption(pubSocket, ZMQ.ZMQ_RCVMORE); if (more == 0) { break; // Last message part } } } if (items[0].isReadable()) { while (true) { Msg msg = ZMQ.recv(subSocket, 0); int msgSize = msg.size(); byte[] buffer = msg.data(); System.out.printf("received on subscriber '%s'\n", new String(buffer, 0, msgSize)); more = ZMQ.getSocketOption(subSocket, ZMQ.ZMQ_RCVMORE); if (more == 0) { publicationsReceived++; break; // Last message part } } } if (iteration == 1) { ZMQ.connect(subSocket, "inproc://someInProcDescriptor"); } if (iteration == 4) { ZMQ.disconnect(subSocket, "inproc://someInProcDescriptor"); } if (iteration > 4 && rc == 0) { break; } Msg channelEnvlp = new Msg("foo".getBytes(ZMQ.CHARSET)); ZMQ.sendMsg(pubSocket, channelEnvlp, ZMQ.ZMQ_SNDMORE); Msg message = new Msg("this is foo!".getBytes(ZMQ.CHARSET)); ZMQ.sendMsg(pubSocket, message, 0); iteration++; } assertEquals(3, publicationsReceived); assertFalse(isSubscribed); ZMQ.close(pubSocket); ZMQ.close(subSocket); } } jeromq-0.6.0/src/test/java/zmq/InprocUnbindTest.java000066400000000000000000000012211455771126300224140ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class InprocUnbindTest { @Test public void testUnbindInproc() { Ctx ctx = ZMQ.init(1); assert (ctx != null); SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(bind, notNullValue()); boolean rc = ZMQ.bind(bind, "inproc://a"); assertThat(rc, is(true)); rc = ZMQ.unbind(bind, "inproc://a"); assertThat(rc, is(true)); ZMQ.close(bind); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/OptionsTest.java000066400000000000000000000305511455771126300214650ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.zeromq.SelectorProviderTest.DefaultSelectorProviderChooser; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ.Socket; import zmq.io.mechanism.Mechanisms; import zmq.io.net.SelectorProviderChooser; import zmq.msg.MsgAllocatorDirect; import zmq.msg.MsgAllocatorThreshold; public class OptionsTest { private Options options; @Before public void setUp() { options = new Options(); } @Test public void testDefaultValues() { assertThat(options.affinity, is(0L)); assertThat(options.allocator, notNullValue()); assertThat(options.allocator, is(instanceOf(MsgAllocatorThreshold.class))); assertThat(options.backlog, is(100)); assertThat(options.conflate, is(false)); } @Test public void testAffinity() { options.setSocketOpt(ZMQ.ZMQ_AFFINITY, 1000L); assertThat(options.getSocketOpt(ZMQ.ZMQ_AFFINITY), is(1000L)); } @Test public void testAllocator() { options.setSocketOpt(ZMQ.ZMQ_MSG_ALLOCATOR, new MsgAllocatorDirect()); assertThat(options.getSocketOpt(ZMQ.ZMQ_MSG_ALLOCATOR), is(options.allocator)); } @Test public void testBacklog() { options.setSocketOpt(ZMQ.ZMQ_BACKLOG, 2000); assertThat(options.getSocketOpt(ZMQ.ZMQ_BACKLOG), is(2000)); } @Test public void testConflate() { options.setSocketOpt(ZMQ.ZMQ_CONFLATE, true); assertThat(options.getSocketOpt(ZMQ.ZMQ_CONFLATE), is(true)); } @Test public void testRate() { options.setSocketOpt(ZMQ.ZMQ_RATE, 10); assertThat(options.getSocketOpt(ZMQ.ZMQ_RATE), is(10)); } @Test public void testRecoveryIvl() { options.setSocketOpt(ZMQ.ZMQ_RECOVERY_IVL, 11); assertThat(options.getSocketOpt(ZMQ.ZMQ_RECOVERY_IVL), is(11)); } @Test public void testMulticastHops() { options.setSocketOpt(ZMQ.ZMQ_MULTICAST_HOPS, 12); assertThat(options.getSocketOpt(ZMQ.ZMQ_MULTICAST_HOPS), is(12)); } @Test public void testPlainUsername() { options.setSocketOpt(ZMQ.ZMQ_CURVE_SERVER, true); String username = "username"; options.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, username); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME), is(username)); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_SERVER), is(false)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MECHANISM), is(Mechanisms.PLAIN)); } @Test public void testPlainPassword() { options.setSocketOpt(ZMQ.ZMQ_CURVE_SERVER, true); String password = "password"; options.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, password); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD), is(password)); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_SERVER), is(false)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MECHANISM), is(Mechanisms.PLAIN)); } @Test public void testPlainUsernameNull() { options.setSocketOpt(ZMQ.ZMQ_CURVE_SERVER, true); options.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, null); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME), nullValue()); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_SERVER), is(false)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MECHANISM), is(Mechanisms.NULL)); } @Test public void testPlainPasswordNull() { options.setSocketOpt(ZMQ.ZMQ_CURVE_SERVER, true); options.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, null); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD), nullValue()); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_SERVER), is(false)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MECHANISM), is(Mechanisms.NULL)); } @Test public void testCurvePublicKey() { byte[] key = new byte[32]; Arrays.fill(key, (byte) 11); options.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, key); assertThat(options.getSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY), is(key)); } @Test public void testCurveSecretKey() { byte[] key = new byte[32]; Arrays.fill(key, (byte) 12); options.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, key); assertThat(options.getSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY), is(key)); } @Test public void testCurveServerKey() { byte[] key = new byte[32]; Arrays.fill(key, (byte) 14); options.setSocketOpt(ZMQ.ZMQ_CURVE_SERVERKEY, key); assertThat(options.getSocketOpt(ZMQ.ZMQ_CURVE_SERVERKEY), is(key)); } @Test public void testGssPlaintext() { options.setSocketOpt(ZMQ.ZMQ_GSSAPI_PLAINTEXT, true); assertThat(options.getSocketOpt(ZMQ.ZMQ_GSSAPI_PLAINTEXT), is(true)); } @Test public void testHeartbeatInterval() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_IVL, 1000); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_IVL), is(1000)); } @Test public void testHeartbeatTimeout() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TIMEOUT, 1001); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TIMEOUT), is(1001)); } @Test public void testHeartbeatTtlRounded() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, 2020); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL), is(2000)); } @Test public void testHeartbeatTtlMin() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, -99); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL), is(0)); } @Test public void testHeartbeatTtlRoundedMin() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, 99); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL), is(0)); } @Test public void testHeartbeatTtlMax() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, 655399); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL), is(655300)); } @Test(expected = IllegalArgumentException.class) public void testHeartbeatTtlOverflow() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, 655400); } @Test(expected = IllegalArgumentException.class) public void testHeartbeatTtlUnderflow() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL, -100); } @Test(expected = IllegalArgumentException.class) public void testHeartbeatIvlUnderflow() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_IVL, -1); } @Test(expected = IllegalArgumentException.class) public void testHeartbeatTimeoutUnderflow() { options.setSocketOpt(ZMQ.ZMQ_HEARTBEAT_TIMEOUT, -1); } @Test(expected = IllegalArgumentException.class) public void testHandshakeIvlUnderflow() { options.setSocketOpt(ZMQ.ZMQ_HANDSHAKE_IVL, -1); } @Test(timeout = 5000) public void testSelectorObject() { try (ZContext ctx = new ZContext(); Socket socket = ctx.createSocket(SocketType.PUB)) { SelectorProviderChooser chooser = new DefaultSelectorProviderChooser(); socket.setSelectorChooser(chooser); Assert.assertEquals(chooser, socket.getSelectorProviderChooser()); } } @Test public void testSelectorClass() { Options opt = new Options(); Class chooser = DefaultSelectorProviderChooser.class; opt.setSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, chooser); Assert.assertTrue(opt.getSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER) instanceof SelectorProviderChooser); } @Test public void testSelectorClassName() { Options opt = new Options(); Class chooser = DefaultSelectorProviderChooser.class; opt.setSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, chooser.getName()); Assert.assertTrue(opt.getSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER) instanceof SelectorProviderChooser); } @Test(expected = IllegalArgumentException.class) public void testSelectorClassNameFailed() { Options opt = new Options(); opt.setSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, String.class.getName()); } @Test(expected = IllegalArgumentException.class) public void testSelectorFailed() { Options opt = new Options(); Assert.assertFalse(opt.setSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, "")); } @Test public void testIdentityOk() { Options opt = new Options(); // Try with a big identity Assert.assertTrue(opt.setSocketOpt(ZMQ.ZMQ_IDENTITY, new byte[255])); } @Test(expected = IllegalArgumentException.class) public void testIdentityFails() { Options opt = new Options(); // Try with a big identity Assert.assertTrue(opt.setSocketOpt(ZMQ.ZMQ_IDENTITY, new byte[256])); } @Test public void testDefaultValue() { assertThat(options.getSocketOpt(ZMQ.ZMQ_GSSAPI_PRINCIPAL), is(options.gssPrincipal)); assertThat(options.getSocketOpt(ZMQ.ZMQ_GSSAPI_SERVICE_PRINCIPAL), is(options.gssServicePrincipal)); assertThat(options.getSocketOpt(ZMQ.ZMQ_HANDSHAKE_IVL), is(options.handshakeIvl)); assertThat(options.getSocketOpt(ZMQ.ZMQ_IDENTITY), is(options.identity)); assertThat(options.getSocketOpt(ZMQ.ZMQ_IMMEDIATE), is(options.immediate)); // assertThat(options.getSocketOpt(ZMQ.ZMQ_TCP_ACCEPT_FILTER), is((Object)options.ipcAcceptFilters)); assertThat(options.getSocketOpt(ZMQ.ZMQ_IPV6), is(options.ipv6)); assertThat(options.getSocketOpt(ZMQ.ZMQ_LAST_ENDPOINT), is(options.lastEndpoint)); assertThat(options.getSocketOpt(ZMQ.ZMQ_LINGER), is(options.linger)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MAXMSGSIZE), is(options.maxMsgSize)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MECHANISM), is(options.mechanism)); assertThat(options.getSocketOpt(ZMQ.ZMQ_MULTICAST_HOPS), is(options.multicastHops)); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD), is(options.plainPassword)); assertThat(options.getSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME), is(options.plainUsername)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RATE), is(options.rate)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RCVBUF), is(options.rcvbuf)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RECONNECT_IVL), is(options.reconnectIvl)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RECONNECT_IVL_MAX), is(options.reconnectIvlMax)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RECOVERY_IVL), is(options.recoveryIvl)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RCVHWM), is(options.recvHwm)); assertThat(options.getSocketOpt(ZMQ.ZMQ_RCVTIMEO), is(options.recvTimeout)); assertThat(options.getSocketOpt(ZMQ.ZMQ_SNDHWM), is(options.sendHwm)); assertThat(options.getSocketOpt(ZMQ.ZMQ_SNDTIMEO), is(options.sendTimeout)); assertThat(options.getSocketOpt(ZMQ.ZMQ_SNDBUF), is(options.sndbuf)); assertThat(options.getSocketOpt(ZMQ.ZMQ_SOCKS_PROXY), is(options.socksProxyAddress)); // assertThat(options.getSocketOpt(ZMQ.ZMQ_TCP_ACCEPT_FILTER), is((Object)options.tcpAcceptFilters)); assertThat(options.getSocketOpt(ZMQ.ZMQ_TCP_KEEPALIVE), is(options.tcpKeepAlive)); assertThat(options.getSocketOpt(ZMQ.ZMQ_TOS), is(options.tos)); assertThat(options.getSocketOpt(ZMQ.ZMQ_TYPE), is(options.type)); assertThat(options.getSocketOpt(ZMQ.ZMQ_ZAP_DOMAIN), is(options.zapDomain)); assertThat(options.getSocketOpt(ZMQ.ZMQ_HANDSHAKE_IVL), is(options.handshakeIvl)); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_IVL), is(options.heartbeatInterval)); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TIMEOUT), is(options.heartbeatTimeout)); assertThat(options.getSocketOpt(ZMQ.ZMQ_HEARTBEAT_TTL), is(options.heartbeatTtl)); assertThat(options.getSocketOpt(ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER), nullValue()); assertThat(options.getSocketOpt(ZMQ.ZMQ_IDENTITY), is(new byte[0])); assertThat(options.getSocketOpt(ZMQ.ZMQ_SELFADDR_PROPERTY_NAME), nullValue()); } } jeromq-0.6.0/src/test/java/zmq/PollTest.java000066400000000000000000000112751455771126300207420ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.channels.Pipe; import java.nio.channels.Pipe.SinkChannel; import java.nio.channels.Pipe.SourceChannel; import java.nio.channels.SelectableChannel; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.UUID; import org.junit.Ignore; import org.junit.Test; import zmq.poll.PollItem; public class PollTest { interface TxRx { R apply(ByteBuffer bb) throws IOException; } @Test(timeout = 1000) public void testPollTcp() throws IOException { ServerSocketChannel server = ServerSocketChannel.open(); assertThat(server, notNullValue()); server.configureBlocking(true); server.socket().bind(null); InetSocketAddress addr = (InetSocketAddress) server.socket().getLocalSocketAddress(); SocketChannel in = SocketChannel.open(); assertThat(in, notNullValue()); in.configureBlocking(false); boolean rc = in.connect(addr); assertThat(rc, is(false)); SocketChannel out = server.accept(); out.configureBlocking(false); rc = in.finishConnect(); assertThat(rc, is(true)); try { assertPoller(in, out, out::write, in::read); } finally { in.close(); out.close(); server.close(); } } @Test(timeout = 1000) public void testPollPipe() throws IOException { Pipe pipe = Pipe.open(); assertThat(pipe, notNullValue()); SinkChannel sink = pipe.sink(); assertThat(sink, notNullValue()); sink.configureBlocking(false); SourceChannel source = pipe.source(); assertThat(source, notNullValue()); source.configureBlocking(false); try { assertPoller(source, sink, sink::write, source::read); } finally { sink.close(); source.close(); } } @Test(timeout = 1000) public void testPollUdp() throws IOException { DatagramChannel in = DatagramChannel.open(); assertThat(in, notNullValue()); in.configureBlocking(false); in.socket().bind(null); InetSocketAddress addr = (InetSocketAddress) in.socket().getLocalSocketAddress(); DatagramChannel out = DatagramChannel.open(); assertThat(out, notNullValue()); out.configureBlocking(false); out.connect(addr); try { assertPoller(in, out, bb -> out.send(bb, addr), in::receive); } finally { in.close(); out.close(); } } private void assertPoller(T in, T out, TxRx tx, TxRx rx) throws IOException { Ctx context = ZMQ.init(1); assertThat(context, notNullValue()); Selector selector = context.createSelector(); assertThat(selector, notNullValue()); PollItem[] items = new PollItem[2]; items[0] = new PollItem(out, ZMQ.ZMQ_POLLOUT); items[1] = new PollItem(in, ZMQ.ZMQ_POLLIN); String payload = UUID.randomUUID().toString(); boolean sending = true; while (true) { int events = ZMQ.poll(selector, items, 1000); if (events < 0) { fail("unable to poll events"); } if (sending && items[0].isWritable()) { sending = false; ByteBuffer bb = ByteBuffer.allocate(payload.length()); bb.put(payload.getBytes()); bb.flip(); int written = tx.apply(bb); assertThat(written, is(payload.length())); } if (!sending && items[1].isReadable()) { ByteBuffer bb = ByteBuffer.allocate(payload.length()); rx.apply(bb); String read = new String(bb.array(), 0, bb.limit()); assertThat(read, is(payload)); break; } } context.closeSelector(selector); ZMQ.term(context); } @Ignore @Test(timeout = 1000) public void testRepeated() throws IOException { for (int idx = 0; idx < 10_000_000; ++idx) { if (idx % 10000 == 0) { System.out.println(idx); } testPollTcp(); testPollUdp(); } } } jeromq-0.6.0/src/test/java/zmq/TermEndpointIpcTest.java000066400000000000000000000004561455771126300230770ustar00rootroot00000000000000package zmq; import java.util.UUID; public class TermEndpointIpcTest extends TestTermEndpoint { @Override protected String endpointWildcard() { return "ipc://*"; } @Override protected String endpointNormal() { return "ipc://" + UUID.randomUUID(); } } jeromq-0.6.0/src/test/java/zmq/TestChannel.java000066400000000000000000000024371455771126300214040ustar00rootroot00000000000000package zmq; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestChannel { @Test public void testRoundtrip() { String address = "inproc://channel"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create bind channel SocketBase bChannel = ZMQ.socket(context, ZMQ.ZMQ_CHANNEL); assertThat(bChannel, notNullValue()); boolean rc = ZMQ.bind(bChannel, address); assertThat(rc, is(true)); // Create connect channel SocketBase cChannel = ZMQ.socket(context, ZMQ.ZMQ_CHANNEL); assertThat(cChannel, notNullValue()); rc = ZMQ.connect(cChannel, address); assertThat(rc, is(true)); int sent = ZMQ.send(cChannel, "HELLO", 0); assertThat(sent, is(5)); Msg msg = ZMQ.recv(bChannel, 0); assertThat(new String(msg.data()), is("HELLO")); sent = ZMQ.send(bChannel, "WORLD", 0); assertThat(sent, is(5)); msg = ZMQ.recv(cChannel, 0); assertThat(new String(msg.data()), is("WORLD")); ZMQ.close(cChannel); ZMQ.close(bChannel); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestClientServer.java000066400000000000000000000156561455771126300224500ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestClientServer { @Test public void testInproc() { System.out.println("Scenario 1"); String address = "inproc://client-server"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // bind server boolean rc = ZMQ.bind(server, address); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.connect(client, address); assertThat(rc, is(true)); // Send a message from client Msg msg = new Msg("X".getBytes()); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); // Recv message on the server side msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int routingId = msg.getRoutingId(); assertThat(routingId, not(0)); // Send message back to client using routing id msg = new Msg("HELLO".getBytes()); msg.setRoutingId(routingId); size = ZMQ.send(server, msg, 0); assertThat(size, is(5)); // Client recv message from server msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("HELLO")); ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } @Test public void testTcp() throws Exception { System.out.println("Scenario 2"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // bind server boolean rc = ZMQ.bind(server, "tcp://*:" + port); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.connect(client, "tcp://localhost:" + port); assertThat(rc, is(true)); // Send a message from client Msg msg = new Msg("X".getBytes()); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); // Recv message on the server side msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int routingId = msg.getRoutingId(); assertThat(routingId, not(0)); // Send message back to client using routing id msg = new Msg("HELLO".getBytes()); msg.setRoutingId(routingId); size = ZMQ.send(server, msg, 0); assertThat(size, is(5)); // Client recv message from server msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("HELLO")); ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } // Client threads loop on send/recv until told to exit static class ClientThread extends Thread { final SocketBase client; ClientThread(SocketBase client) { this.client = client; } public void run() { for (int count = 0; count < 15000; count++) { Msg msg = new Msg("0".getBytes()); int rc = ZMQ.send(client, msg, 0); assertThat(rc, is(1)); } Msg msg = new Msg("1".getBytes()); int rc = ZMQ.send(client, msg, 0); assertThat(rc, is(1)); } } @Test public void testThreadSafe() throws Exception { System.out.println("Scenario 3"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); boolean rc = ZMQ.bind(server, "tcp://*:" + port); assertThat(rc, is(true)); SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.connect(client, "tcp://localhost:" + port); assertThat(rc, is(true)); ClientThread t1 = new ClientThread(client); ClientThread t2 = new ClientThread(client); t1.start(); t2.start(); int threadsCompleted = 0; while (threadsCompleted < 2) { Msg msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); if (msg.data()[0] == '1') { threadsCompleted++; // Thread ended } } t1.join(); t2.join(); ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } @Test public void testAsRouterType() throws Exception { System.out.println("Scenario 2"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // Make wire type of the socket as ROUTER boolean rc = server.setSocketOpt(ZMQ.ZMQ_AS_TYPE, ZMQ.ZMQ_ROUTER); assertThat(rc, is(true)); // bind server rc = ZMQ.bind(server, "tcp://*:" + port); assertThat(rc, is(true)); // create a dealer SocketBase dealer = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); rc = ZMQ.connect(dealer, "tcp://localhost:" + port); assertThat(rc, is(true)); // Send a message from dealer Msg msg = new Msg("X".getBytes()); int size = ZMQ.send(dealer, msg, 0); assertThat(size, is(1)); // Recv message on the server side msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int routingId = msg.getRoutingId(); assertThat(routingId, not(0)); // Send message back to dealer using routing id msg = new Msg("HELLO".getBytes()); msg.setRoutingId(routingId); size = ZMQ.send(server, msg, 0); assertThat(size, is(5)); // Dealer recv message from server msg = ZMQ.recv(dealer, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("HELLO")); ZMQ.close(dealer); ZMQ.close(server); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestConnectDelay.java000066400000000000000000000173431455771126300224060ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.util.Utils; @SuppressWarnings("deprecation") public class TestConnectDelay { @Test public void testConnectDelay1() throws Exception { System.out.println("Scenario 1"); // TEST 1. // First we're going to attempt to send messages to two // pipes, one connected, the other not. We should see // the PUSH load balancing to both pipes, and hence half // of the messages getting queued, as connect() creates a // pipe immediately. int pushPort1 = Utils.findOpenPort(); int pushPort2 = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); SocketBase to = ZMQ.socket(context, ZMQ.ZMQ_PULL); assertThat(to, notNullValue()); int val = 0; boolean rc = ZMQ.setSocketOption(to, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); rc = ZMQ.bind(to, "tcp://*:" + pushPort1); assertThat(rc, is(true)); // Create a socket pushing to two endpoints - only 1 message should arrive. SocketBase from = ZMQ.socket(context, ZMQ.ZMQ_PUSH); assertThat(from, notNullValue()); val = 0; rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // This pipe will not connect rc = ZMQ.connect(from, "tcp://localhost:" + pushPort2); assertThat(rc, is(true)); // This pipe will rc = ZMQ.connect(from, "tcp://localhost:" + pushPort1); assertThat(rc, is(true)); // We send 10 messages, 5 should just get stuck in the queue // for the not-yet-connected pipe for (int i = 0; i < 10; ++i) { String message = "message "; message += ('0' + i); int sent = ZMQ.send(from, message, 0); assertThat(sent >= 0, is(true)); } ZMQ.sleep(1); // We now consume from the connected pipe // - we should see just 5 int timeout = 250; ZMQ.setSocketOption(to, ZMQ.ZMQ_RCVTIMEO, timeout); int seen = 0; for (int i = 0; i < 10; ++i) { Msg msg = ZMQ.recv(to, 0); if (msg == null) { break; // Break when we didn't get a message } seen++; } assertThat(seen, is(5)); ZMQ.close(from); ZMQ.close(to); ZMQ.term(context); } @Test public void testConnectDelay2() throws Exception { System.out.println("Scenario 2"); // TEST 2 // This time we will do the same thing, connect two pipes, // one of which will succeed in connecting to a bound // receiver, the other of which will fail. However, we will // also set the delay attach on connect flag, which should // cause the pipe attachment to be delayed until the connection // succeeds. int validPort = Utils.findOpenPort(); int invalidPort = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); SocketBase to = ZMQ.socket(context, ZMQ.ZMQ_PULL); assertThat(to, notNullValue()); boolean rc = ZMQ.bind(to, "tcp://*:" + validPort); assertThat(rc, is(true)); int val = 0; rc = ZMQ.setSocketOption(to, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // Create a socket pushing to two endpoints - all messages should arrive. SocketBase from = ZMQ.socket(context, ZMQ.ZMQ_PUSH); assertThat(from, notNullValue()); val = 0; rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_LINGER, val); assertThat(rc, is(true)); // Set the key flag rc = ZMQ.setSocketOption(from, ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT, true); assertThat(rc, is(true)); // Connect to the invalid socket rc = ZMQ.connect(from, "tcp://localhost:" + invalidPort); assertThat(rc, is(true)); // Connect to the valid socket rc = ZMQ.connect(from, "tcp://localhost:" + validPort); assertThat(rc, is(true)); for (int i = 0; i < 10; ++i) { String message = "message "; message += ('0' + i); int sent = ZMQ.send(from, message, 0); assertThat(sent, is(message.length())); } int timeout = 250; ZMQ.setSocketOption(to, ZMQ.ZMQ_RCVTIMEO, timeout); int seen = 0; for (int i = 0; i < 10; ++i) { Msg msg = ZMQ.recv(to, 0); if (msg == null) { break; } seen++; } assertThat(seen, is(10)); ZMQ.close(from); ZMQ.close(to); ZMQ.term(context); } @Test(timeout = 5000) public void testConnectDelay3() throws Exception { System.out.print("Scenario 3"); // TEST 3 // This time we want to validate that the same blocking behaviour // occurs with an existing connection that is broken. We will send // messages to a connected pipe, disconnect and verify the messages // block. Then we reconnect and verify messages flow again. int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); SocketBase backend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(backend, notNullValue()); SocketBase frontend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(frontend, notNullValue()); final int linger = 0; ZMQ.setSocketOption(backend, ZMQ.ZMQ_LINGER, linger); ZMQ.setSocketOption(frontend, ZMQ.ZMQ_LINGER, linger); // Frontend connects to backend using DELAY_ATTACH_ON_CONNECT ZMQ.setSocketOption(frontend, ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT, true); boolean rc = ZMQ.bind(backend, "tcp://*:" + port); assertThat(rc, is(true)); rc = ZMQ.connect(frontend, "tcp://localhost:" + port); assertThat(rc, is(true)); System.out.print("."); // Ping backend to frontend so we know when the connection is up int sent = ZMQ.send(backend, "Hello", 0); assertThat(sent, is(5)); System.out.print("Ping"); Msg msg = ZMQ.recv(frontend, 0); System.out.print("."); assertThat(msg.size(), is(5)); // Send message from frontend to backend sent = ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT); assertThat(sent, is(5)); System.out.print("Message sent"); ZMQ.close(backend); System.out.print("."); System.out.print("Message send fail"); // Send a message, should fail // There's no way to do this except with a sleep and a loop while (ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Recreate backend socket backend = ZMQ.socket(context, ZMQ.ZMQ_DEALER); ZMQ.setSocketOption(backend, ZMQ.ZMQ_LINGER, linger); rc = ZMQ.bind(backend, "tcp://*:" + port); assertThat(rc, is(true)); System.out.print("."); // Ping backend to frontend so we know when the connection is up sent = ZMQ.send(backend, "Hello", 0); assertThat(sent, is(5)); System.out.print("Ping"); msg = ZMQ.recv(frontend, 0); System.out.print("."); assertThat(msg.size(), is(5)); System.out.print("Message sent"); // After the reconnect, should succeed sent = ZMQ.send(frontend, "Hello", ZMQ.ZMQ_DONTWAIT); assertThat(sent, is(5)); System.out.print("."); ZMQ.close(backend); ZMQ.close(frontend); ZMQ.term(context); System.out.println("Done"); } } jeromq-0.6.0/src/test/java/zmq/TestConnectResolve.java000066400000000000000000000016241455771126300227620ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.util.Utils; public class TestConnectResolve { @Test public void testConnectResolve() throws IOException { int port = Utils.findOpenPort(); System.out.println("test_connect_resolve running...\n"); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Create pair of socket, each with high watermark of 2. Thus the total // buffer space should be 4 messages. SocketBase sock = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(sock, notNullValue()); boolean brc = ZMQ.connect(sock, "tcp://localhost:" + port); assertThat(brc, is(true)); ZMQ.close(sock); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestDisconnectMsg.java000066400000000000000000000063171455771126300225750ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestDisconnectMsg { void test(String address) { Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // set router socket options boolean rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_DISCONNECT_MSG, "D"); assertThat(rc, is(true)); // bind server rc = ZMQ.bind(server, address); assertThat(rc, is(true)); // Create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HELLO_MSG, "H"); assertThat(rc, is(true)); rc = ZMQ.connect(client, address); assertThat(rc, is(true)); // Receive the hello message from client Msg msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("H")); // Kill the client ZMQ.close(client); // Receive the disconnect message msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("D")); // Clean up. ZMQ.close(server); ZMQ.term(context); } @Test public void testTcp() throws Exception { System.out.println("Scenario 1"); int port = Utils.findOpenPort(); String address = "tcp://localhost:" + port; test(address); } @Test public void testInproc() { System.out.println("Scenario 2"); test("inproc://disconnect-msg"); } @Test public void testInprocDisconnect() { System.out.println("Scenario 3"); String address = "inproc://disconnect-msg"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // set server socket options boolean rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_DISCONNECT_MSG, "D"); assertThat(rc, is(true)); // bind server rc = ZMQ.bind(server, address); assertThat(rc, is(true)); // Create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HELLO_MSG, "H"); assertThat(rc, is(true)); rc = ZMQ.connect(client, address); assertThat(rc, is(true)); // Receive the hello message from client Msg msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("H")); // disconnect the client rc = client.termEndpoint(address); assertThat(rc, is(true)); // Receive the disconnect message msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("D")); // Clean up. ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestHelloMsg.java000066400000000000000000000071531455771126300215460ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestHelloMsg { @Test public void testTcp() throws Exception { System.out.println("Scenario 1"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a routergit s SocketBase router = ZMQ.socket(context, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); // set router socket options boolean rc = ZMQ.setSocketOption(router, ZMQ.ZMQ_HELLO_MSG, "H"); assertThat(rc, is(true)); // bind router rc = ZMQ.bind(router, "tcp://*:" + port); assertThat(rc, is(true)); // create a dealer SocketBase dealer = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); rc = ZMQ.connect(dealer, "tcp://localhost:" + port); assertThat(rc, is(true)); // receive hello message from router Msg msg = ZMQ.recv(dealer, 0); assertThat(new String(msg.data()), is("H")); ZMQ.close(dealer); ZMQ.close(router); ZMQ.term(context); } @Test public void testInproc() { System.out.println("Scenario 2"); String address = "inproc://hello-msg"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a router SocketBase router = ZMQ.socket(context, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); // set router socket options boolean rc = ZMQ.setSocketOption(router, ZMQ.ZMQ_HELLO_MSG, "H"); assertThat(rc, is(true)); // bind router rc = ZMQ.bind(router, address); assertThat(rc, is(true)); // create a dealer SocketBase dealer = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); rc = ZMQ.connect(dealer, address); assertThat(rc, is(true)); // receive hello message from router Msg msg = ZMQ.recv(dealer, 0); assertThat(new String(msg.data()), is("H")); ZMQ.close(dealer); ZMQ.close(router); ZMQ.term(context); } @Test public void testInprocLateBind() { System.out.println("Scenario 3"); String address = "inproc://hello-msg"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(server, notNullValue()); // set server socket options boolean rc = ZMQ.setSocketOption(server, ZMQ.ZMQ_HELLO_MSG, "W"); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_DEALER); assertThat(client, notNullValue()); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HELLO_MSG, "H"); assertThat(rc, is(true)); rc = ZMQ.connect(client, address); assertThat(rc, is(true)); // bind server after the client rc = ZMQ.bind(server, address); assertThat(rc, is(true)); // Receive the welcome message from server Msg msg = ZMQ.recv(client, 0); assertThat(new String(msg.data()), is("W")); // Receive the hello message from client msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("H")); ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestHiccupMsg.java000066400000000000000000000030351455771126300217110ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestHiccupMsg { @Test public void test() throws Exception { int port = Utils.findOpenPort(); String address = "tcp://localhost:" + port; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // bind server boolean rc = ZMQ.bind(server, address); assertThat(rc, is(true)); // Create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HELLO_MSG, "HELLO"); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_HICCUP_MSG, "HICCUP"); assertThat(rc, is(true)); rc = ZMQ.connect(client, address); assertThat(rc, is(true)); // Receive the hello message from client Msg msg = ZMQ.recv(server, 0); assertThat(new String(msg.data()), is("HELLO")); // Kill the server ZMQ.close(server); // Receive the hiccup message msg = ZMQ.recv(client, 0); assertThat(new String(msg.data()), is("HICCUP")); // Clean up. ZMQ.close(client); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestHwm.java000066400000000000000000000035451455771126300205700ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class TestHwm { @Test public void testHwm() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); int rc; boolean brc; // Create pair of socket, each with high watermark of 2. Thus the total // buffer space should be 4 messages. SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(sb, notNullValue()); int hwm = 2; ZMQ.setSocketOption(sb, ZMQ.ZMQ_RCVHWM, hwm); brc = ZMQ.bind(sb, "inproc://a"); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(sc, notNullValue()); ZMQ.setSocketOption(sc, ZMQ.ZMQ_SNDHWM, hwm); brc = ZMQ.connect(sc, "inproc://a"); assertThat(brc, is(true)); // Try to send 10 messages. Only 4 should succeed. for (int i = 0; i < 10; i++) { rc = ZMQ.send(sc, null, 0, ZMQ.ZMQ_DONTWAIT); if (i < 4) { assertThat(rc, is(0)); } else { assertThat(rc, is(-1)); } } Msg m; // There should be now 4 messages pending, consume them. for (int i = 0; i != 4; i++) { m = ZMQ.recv(sb, 0); assertThat(m, notNullValue()); assertThat(m.size(), is(0)); } // Now it should be possible to send one more. rc = ZMQ.send(sc, null, 0, 0); assertThat(rc, is(0)); // Consume the remaining message. m = ZMQ.recv(sb, 0); assertThat(rc, notNullValue()); assertThat(m.size(), is(0)); ZMQ.close(sc); ZMQ.close(sb); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestLastEndpoint.java000066400000000000000000000063671455771126300224460ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.Test; import zmq.util.Utils; public class TestLastEndpoint { static void bindAndVerify(SocketBase s, String endpoint) { boolean brc = ZMQ.bind(s, endpoint); assertThat(brc, is(true)); String stest = (String) ZMQ.getSocketOptionExt(s, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(stest, is(endpoint)); } @Test public void testLastEndpoint() throws IOException { int port1 = Utils.findOpenPort(); int port2 = Utils.findOpenPort(); // Create the infrastructure Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(sb, notNullValue()); bindAndVerify(sb, "tcp://127.0.0.1:" + port1); bindAndVerify(sb, "tcp://127.0.0.1:" + port2); sb.close(); ctx.terminate(); } @Test public void testLastEndpointWildcardIpc() { // Create the infrastructure Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase socket = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(socket, notNullValue()); boolean brc = ZMQ.bind(socket, "ipc://*"); assertThat(brc, is(true)); String stest = (String) ZMQ.getSocketOptionExt(socket, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(stest, is(not("ipc://*"))); assertThat(stest, is(not("ipc://localhost:0"))); Pattern pattern = Pattern.compile("ipc://.*", Pattern.DOTALL); Matcher matcher = pattern.matcher(stest); assertThat(stest, matcher.matches(), is(true)); socket.close(); ctx.terminate(); } @Test public void testLastEndpointWildcardTcp() { // Create the infrastructure Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase socket = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(socket, notNullValue()); boolean brc = ZMQ.bind(socket, "tcp://127.0.0.1:*"); assertThat(brc, is(true)); String stest = (String) ZMQ.getSocketOptionExt(socket, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(stest, is(not("tcp://127.0.0.1:*"))); assertThat(stest, is(not("tcp://127.0.0.1:0"))); assertThat(stest.matches("tcp://127.0.0.1:\\d+"), is(true)); socket.close(); ctx.terminate(); } @Test public void testLastEndpointAllWildcards() { // Create the infrastructure Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase socket = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(socket, notNullValue()); boolean brc = ZMQ.bind(socket, "tcp://*:*"); assertThat(brc, is(true)); String stest = (String) ZMQ.getSocketOptionExt(socket, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(stest, is(not("tcp://0.0.0.0:0"))); assertThat(stest.matches("tcp://0.0.0.0:\\d+"), is(true)); socket.close(); ctx.terminate(); } } jeromq-0.6.0/src/test/java/zmq/TestMonitor.java000066400000000000000000000262421455771126300214630ustar00rootroot00000000000000package zmq; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class TestMonitor { static class SocketMonitor implements Callable { private final Ctx ctx; private final String monitorAddr; private final CountDownLatch startLatch; private final CountDownLatch activeLatch; private final CountDownLatch monitorStopLatch; private int events; public SocketMonitor(Ctx ctx, String monitorAddr) { this.ctx = ctx; this.monitorAddr = monitorAddr; this.startLatch = new CountDownLatch(1); this.activeLatch = new CountDownLatch(1); this.monitorStopLatch = new CountDownLatch(1); events = 0; } @Override public AssertionError call() { SocketBase s = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); try { boolean rc = s.connect(monitorAddr); assertThat(rc, is(true)); startLatch.countDown(); // Only some exceptional events could fire while (true) { ZMQ.Event event = ZMQ.Event.read(s); // Signals the context termination if (event == null && s.errno() == ZError.ETERM) { break; } assertThat(event, notNullValue()); if (event.event == ZMQ.ZMQ_EVENT_CONNECTED || event.event == ZMQ.ZMQ_EVENT_ACCEPTED) { activeLatch.countDown(); } if (event.event == ZMQ.ZMQ_EVENT_MONITOR_STOPPED) { monitorStopLatch.countDown(); } switch (event.event) { // listener specific case ZMQ.ZMQ_EVENT_LISTENING: case ZMQ.ZMQ_EVENT_ACCEPTED: // connecter specific case ZMQ.ZMQ_EVENT_CONNECTED: case ZMQ.ZMQ_EVENT_CONNECT_DELAYED: // generic - either end of the socket case ZMQ.ZMQ_EVENT_CLOSE_FAILED: case ZMQ.ZMQ_EVENT_CLOSED: case ZMQ.ZMQ_EVENT_DISCONNECTED: case ZMQ.ZMQ_EVENT_MONITOR_STOPPED: case ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL: case ZMQ.ZMQ_EVENT_CONNECT_RETRIED: events |= event.event; break; default: // out of band / unexpected event fail("Unknown Event " + event.event); } } } catch (AssertionError ex) { return ex; } finally { s.close(); } return null; } } private final ExecutorService executor = Executors.newFixedThreadPool(4); @Test(timeout = 1000) public void testFailed() { String addr = "tcp://127.0.0.1:*"; Ctx ctx = ZMQ.init(1); SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); try { assertThat(rep, notNullValue()); boolean rc = ZMQ.monitorSocket(rep, addr, 0); assertThat(rc, is(false)); assertThat(rep.errno.get(), is(ZError.EPROTONOSUPPORT)); } finally { rep.close(); ctx.terminate(); } } @Test(timeout = 1000) public void testMonitor() throws Exception { String addr = "tcp://127.0.0.1:*"; SocketMonitor[] threads = new SocketMonitor[3]; // Create the infrastructure Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // set sockets monitors threads[0] = new SocketMonitor(ctx, "inproc://monitor.rep"); threads[1] = new SocketMonitor(ctx, "inproc://monitor.req"); threads[2] = new SocketMonitor(ctx, "inproc://monitor.req2"); Future[] futures = Arrays.stream(threads).map(executor::submit).toArray(Future[]::new); executor.shutdown(); Arrays.stream(threads).map(sm -> sm.startLatch).forEach(this::timeOut); // Check that all tasks are still running assertThat(Arrays.stream(futures).filter(this::checkTaskAlive).count(), is(3L)); // REP socket monitor, all events SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); boolean rc = ZMQ.monitorSocket(rep, "inproc://monitor.rep", ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = ZMQ.bind(rep, addr); assertThat(rc, is(true)); // Resolve the effective listening address addr = (String) ZMQ.getSocketOptionExt(rep, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(addr, notNullValue()); // 1st REQ socket, monitor all events SocketBase req = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); rc = ZMQ.monitorSocket(req, "inproc://monitor.req", ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = ZMQ.connect(req, addr); assertThat(rc, is(true)); // 2nd REQ socket, monitor connected and monitor stop events only SocketBase req2 = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req2, notNullValue()); rc = ZMQ.monitorSocket(req2, "inproc://monitor.req2", ZMQ.ZMQ_EVENT_CONNECTED | ZMQ.ZMQ_EVENT_MONITOR_STOPPED); assertThat(rc, is(true)); rc = ZMQ.connect(req2, addr); assertThat(rc, is(true)); // Plays the dialog between rep and req Arrays.stream(threads).map(sm -> sm.activeLatch).forEach(this::timeOut); Helper.bounce(rep, req); // Close the REP socket ZMQ.setSocketOption(rep, ZMQ.ZMQ_LINGER, 0); ZMQ.close(rep); // Close the REQ socket ZMQ.setSocketOption(req, ZMQ.ZMQ_LINGER, 0); ZMQ.close(req); // Close the 2nd REQ socket ZMQ.setSocketOption(req2, ZMQ.ZMQ_LINGER, 0); ZMQ.close(req2); // Wait for end of monitors, stopped by linger thread Arrays.stream(threads).map(sm -> sm.monitorStopLatch).forEach(this::timeOut); ZMQ.term(ctx); rc = executor.awaitTermination(1, TimeUnit.SECONDS); assertThat(rc, is(true)); // Check that all tasks are finished assertThat(Arrays.stream(futures).filter(this::checkTaskAlive).count(), is(0L)); // Expected REP socket events // We expect to at least observe these events assertTrue((threads[0].events & ZMQ.ZMQ_EVENT_LISTENING) > 0); assertTrue((threads[0].events & ZMQ.ZMQ_EVENT_ACCEPTED) > 0); assertTrue((threads[0].events & ZMQ.ZMQ_EVENT_CLOSED) > 0); assertTrue((threads[0].events & ZMQ.ZMQ_EVENT_MONITOR_STOPPED) > 0); assertTrue((threads[0].events & ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL) > 0); // Expected REQ socket events assertTrue((threads[1].events & ZMQ.ZMQ_EVENT_CONNECT_DELAYED) > 0); assertTrue((threads[1].events & ZMQ.ZMQ_EVENT_CONNECTED) > 0); assertTrue((threads[1].events & ZMQ.ZMQ_EVENT_MONITOR_STOPPED) > 0); assertTrue((threads[1].events & ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL) > 0); // Expected 2nd REQ socket events assertEquals(ZMQ.ZMQ_EVENT_CONNECTED | ZMQ.ZMQ_EVENT_MONITOR_STOPPED, threads[2].events); } private boolean checkTaskAlive(Future f) { try { if (f.isDone()) { AssertionError error = f.get(); if (error != null) { throw error; } else { return false; } } else { return true; } } catch (InterruptedException ex) { throw new IllegalStateException("Interrupted", ex); } catch (ExecutionException ex) { throw new IllegalStateException("Failed future", ex.getCause()); } } private void timeOut(CountDownLatch l) { try { boolean rc = l.await(1, TimeUnit.SECONDS); assertTrue(rc); } catch (InterruptedException ex) { throw new IllegalStateException("Interrupted", ex); } } @Test(timeout = 1000) public void testCustomMonitor() throws InterruptedException { String addr = "tcp://127.0.0.1:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); CountDownLatch listeningLatch = new CountDownLatch(1); CountDownLatch closedLatch = new CountDownLatch(1); CountDownLatch stoppedLatch = new CountDownLatch(1); boolean rc = rep.setEventHook(e -> { switch (e.event) { case ZMQ.ZMQ_EVENT_LISTENING: assertThat(Thread.currentThread().getName(), is("Time-limited test")); listeningLatch.countDown(); break; case ZMQ.ZMQ_EVENT_CLOSED: assertThat(Thread.currentThread().getName(), is("iothread-2")); closedLatch.countDown(); break; case ZMQ.ZMQ_EVENT_MONITOR_STOPPED: assertThat(Thread.currentThread().getName(), is("reaper-1")); stoppedLatch.countDown(); break; default: fail(); } }, ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = ZMQ.bind(rep, addr); assertThat(rc, is(true)); rc = listeningLatch.await(1, TimeUnit.SECONDS); assertThat(rc, is(true)); rep.close(); rc = closedLatch.await(1, TimeUnit.SECONDS); assertThat(rc, is(true)); rc = stoppedLatch.await(1, TimeUnit.SECONDS); assertThat(rc, is(true)); ctx.terminate(); } @Test(timeout = 1000) public void testDisableMonitor() { String addr = "tcp://127.0.0.1:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); boolean rc = rep.setEventHook(e -> { assertThat(e.event, is(ZMQ.ZMQ_EVENT_MONITOR_STOPPED)); assertThat(Thread.currentThread().getName(), is("Time-limited test")); }, ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = rep.monitor(null, ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = ZMQ.bind(rep, addr); assertThat(rc, is(true)); rep.close(); ctx.terminate(); } } jeromq-0.6.0/src/test/java/zmq/TestMsg.java000066400000000000000000000133341455771126300205600ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import java.util.Arrays; import org.junit.Test; import zmq.util.function.Function; public class TestMsg { private final Function allocator; public TestMsg() { this(ByteBuffer::allocateDirect); } protected TestMsg(Function allocator) { this.allocator = allocator; } @Test(expected = IllegalArgumentException.class) public void shouldThrowForNullByteBuffer() { new Msg((ByteBuffer) null); } @Test public void shouldWorkForFlippedBuffers() { ByteBuffer buffer = ByteBuffer.allocate(10); buffer.putChar('a'); buffer.putChar('b'); buffer.putChar('c'); buffer.flip(); new Msg(buffer); } @Test public void testGetBytes() { final Msg msg = initMsg(); final byte[] dst = new byte[3]; msg.getBytes(0, dst, 0, 3); assertThat(dst, is(new byte[] { 0, 1, 2 })); } @Test public void testGetBytesIndex() { final Msg msg = initMsg(); final byte[] dst = new byte[4]; msg.getBytes(1, dst, 0, 4); assertThat(dst, is(new byte[] { 1, 2, 3, 4 })); } @Test public void testGetBytesLength() { final Msg msg = initMsg(); final byte[] dst = new byte[5]; msg.getBytes(2, dst, 0, 2); assertThat(dst, is(new byte[] { 2, 3, 0, 0, 0 })); } @Test public void testGetBytesOffset() { final Msg msg = initMsg(); final byte[] dst = new byte[6]; msg.getBytes(3, dst, 1, 2); assertThat(dst, is(new byte[] { 0, 3, 4, 0, 0, 0 })); } @Test public void testPutString() { final Msg msg = initMsg(); msg.putShortString("data"); final byte[] dst = new byte[5]; msg.getBytes(0, dst, 0, 5); assertThat(dst, is(new byte[] { 4, 'd', 'a', 't', 'a' })); } @Test public void testPutStringInBuilder() { final Msg.Builder msg = new Msg.Builder(); msg.putShortString("data"); final byte[] dst = new byte[5]; msg.build().getBytes(0, dst, 0, 5); assertThat(dst, is(new byte[] { 4, 'd', 'a', 't', 'a' })); } @Test(expected = IllegalArgumentException.class) public void testPutStringLongerThan255() { final Msg msg = initMsg(); char[] charArray = new char[256]; Arrays.fill(charArray, ' '); String str = new String(charArray); msg.putShortString(str); } @Test(expected = IllegalArgumentException.class) public void testPutStringLongerThan255InBuilder() { final Msg.Builder msg = new Msg.Builder(); char[] charArray = new char[256]; Arrays.fill(charArray, ' '); String str = new String(charArray); msg.putShortString(str); } protected Msg initMsg() { return initDirectMsg(allocator); } Msg initDirectMsg(Function allocator) { int size = 30; final ByteBuffer buffer = allocator.apply(size); for (int idx = 0; idx < size; ++idx) { buffer.put((byte) idx); } buffer.position(0); return new Msg(buffer); } // Check that data returned by Msg#getBytes(int, byte[], int, int) and Msg#get(int) are // consistent. @Test public void testGetBytesSameAsGet() { Msg msg1 = new Msg(new byte[] {42}); Msg msg2 = new Msg(msg1); msg2.put(5); byte firstByte = msg2.get(0); byte[] data = new byte[1]; msg2.getBytes(0, data, 0, 1); assertThat(data[0], is(firstByte)); } // Check that Msg#data() is correct when the backing array has an offset. @Test public void testDataNonZeroOffset() { byte[] data = new byte[]{10, 11, 12}; ByteBuffer buffer = ByteBuffer.wrap(data, 1, 2).slice(); Msg msg = new Msg(buffer); assertThat(msg.data(), is(new byte[]{11, 12})); } // Check that Msg#data() is correct when the end of the backing array is not used by the buffer. @Test public void testDataArrayExtendsFurther() { byte[] data = new byte[]{10, 11, 12}; ByteBuffer buffer = ByteBuffer.wrap(data, 0, 2).slice(); Msg msg = new Msg(buffer); assertThat(msg.data(), is(new byte[]{10, 11})); } // Check that data returned by Msg#getBytes(int, byte[], int, int) is correct when the backing // array has an offset. @Test public void testGetBytesNonZeroOffset() { byte[] data = new byte[]{10, 11, 12}; ByteBuffer buffer = ByteBuffer.wrap(data, 1, 2).slice(); Msg msg = new Msg(buffer); byte[] gotData = new byte[2]; msg.getBytes(0, gotData, 0, 2); assertThat(msg.data(), is(new byte[]{11, 12})); } // Check that data returned by Msg#getBytes(int, byte[], int, int) is correct when the end of // the backing array is not used by the buffer. @Test public void testGetBytesArrayExtendsFurther() { byte[] data = new byte[]{10, 11, 12}; ByteBuffer buffer = ByteBuffer.wrap(data, 0, 2).slice(); Msg msg = new Msg(buffer); byte[] gotData = new byte[2]; msg.getBytes(0, gotData, 0, 2); assertThat(msg.data(), is(new byte[]{10, 11})); } // Check that Msg#data() doesn't make unnecessary copies. @Test public void testDataNoCopy() { byte[] data = new byte[]{10, 11, 12}; Msg msg = new Msg(data); assertThat(msg.data(), sameInstance(data)); } } jeromq-0.6.0/src/test/java/zmq/TestMsgDirect.java000066400000000000000000000002461455771126300217110ustar00rootroot00000000000000package zmq; import java.nio.ByteBuffer; public class TestMsgDirect extends TestMsg { public TestMsgDirect() { super(ByteBuffer::allocate); } } jeromq-0.6.0/src/test/java/zmq/TestMsgFlags.java000066400000000000000000000031371455771126300215350ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class TestMsgFlags { // Create REQ/ROUTER wiring. @Test public void testMsgFlags() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(sb, notNullValue()); boolean brc = ZMQ.bind(sb, "inproc://a"); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(sc, notNullValue()); brc = ZMQ.connect(sc, "inproc://a"); assertThat(brc, is(true)); int rc; // Send 2-part message. rc = ZMQ.send(sc, "A", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(1)); rc = ZMQ.send(sc, "B", 0); assertThat(rc, is(1)); // Identity comes first. Msg msg = ZMQ.recvMsg(sb, 0); int more = ZMQ.getMessageOption(msg, ZMQ.ZMQ_MORE); assertThat(more, is(1)); // Then the first part of the message body. msg = ZMQ.recvMsg(sb, 0); assertThat(rc, is(1)); more = ZMQ.getMessageOption(msg, ZMQ.ZMQ_MORE); assertThat(more, is(1)); // And finally, the second part of the message body. msg = ZMQ.recvMsg(sb, 0); assertThat(rc, is(1)); more = ZMQ.getMessageOption(msg, ZMQ.ZMQ_MORE); assertThat(more, is(0)); // Tear down the wiring. ZMQ.close(sb); ZMQ.close(sc); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestPeer.java000066400000000000000000000122061455771126300207220ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; public class TestPeer { @Test public void testInproc() { System.out.println("Scenario 1"); String address = "inproc://peer-to-peer"; Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a bind SocketBase bind = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(bind, notNullValue()); // bind bind boolean rc = ZMQ.bind(bind, address); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(client, notNullValue()); int serverRoutingId = ZMQ.connectPeer(client, address); assertThat(serverRoutingId, not(0)); // Send a message from client Msg msg = new Msg("X".getBytes()); msg.setRoutingId(serverRoutingId); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); // Recv message on the bind side msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int clientRoutingId = msg.getRoutingId(); assertThat(serverRoutingId, not(0)); // Send message back to client using routing id msg = new Msg("HELLO".getBytes()); msg.setRoutingId(clientRoutingId); size = ZMQ.send(bind, msg, 0); assertThat(size, is(5)); // Client recv message from bind msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("HELLO")); ZMQ.close(client); ZMQ.close(bind); ZMQ.term(context); } @Test public void testTcp() throws Exception { System.out.println("Scenario 2"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a bind SocketBase bind = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(bind, notNullValue()); // bind bind boolean rc = ZMQ.bind(bind, "tcp://*:" + port); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(client, notNullValue()); int serverRoutingId = ZMQ.connectPeer(client, "tcp://localhost:" + port); assertThat(serverRoutingId, not(0)); // Send a message from client Msg msg = new Msg("X".getBytes()); msg.setRoutingId(serverRoutingId); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); // Recv message on the bind side msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int clientRoutingId = msg.getRoutingId(); assertThat(serverRoutingId, not(0)); // Send message back to client using routing id msg = new Msg("HELLO".getBytes()); msg.setRoutingId(clientRoutingId); size = ZMQ.send(bind, msg, 0); assertThat(size, is(5)); // Client recv message from bind msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("HELLO")); ZMQ.close(client); ZMQ.close(bind); ZMQ.term(context); } @Test public void testTcpDisconnect() throws Exception { System.out.println("Scenario 3"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a bind SocketBase bind = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(bind, notNullValue()); // bind bind boolean rc = ZMQ.bind(bind, "tcp://*:" + port); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_PEER); assertThat(client, notNullValue()); int serverRoutingId = ZMQ.connectPeer(client, "tcp://localhost:" + port); assertThat(serverRoutingId, not(0)); // Send a message from client Msg msg = new Msg("X".getBytes()); msg.setRoutingId(serverRoutingId); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); // Recv message on the bind side msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data()), is("X")); int clientRoutingId = msg.getRoutingId(); assertThat(serverRoutingId, not(0)); boolean disconnectResult = ZMQ.disconnectPeer(bind, clientRoutingId); assertThat(disconnectResult, is(true)); // Send message back to client fails after disconnect msg = new Msg("HELLO".getBytes()); msg.setRoutingId(clientRoutingId); size = ZMQ.send(bind, msg, 0); assertThat(size, is(-1)); ZMQ.close(client); ZMQ.close(bind); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestRadioDish.java000066400000000000000000000055251455771126300217030ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestRadioDish { @Test public void testTcp() throws Exception { System.out.println("Scenario 1"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a radio SocketBase radio = ZMQ.socket(context, ZMQ.ZMQ_RADIO); assertThat(radio, notNullValue()); // bind server boolean rc = ZMQ.bind(radio, "tcp://*:" + port); assertThat(rc, is(true)); // create a dish SocketBase dish = ZMQ.socket(context, ZMQ.ZMQ_DISH); assertThat(dish, notNullValue()); // Joining rc = dish.join("Movies"); assertThat(rc, is(true)); // Connecting rc = ZMQ.connect(dish, "tcp://localhost:" + port); assertThat(rc, is(true)); ZMQ.msleep(100); // This is not going to be sent as dish only subscribe to "Movies" Msg msg = new Msg("Friends".getBytes()); msg.setGroup("TV"); rc = radio.send(msg, 0); assertThat(rc, is(true)); // This is going to be sent to the dish msg = new Msg("Godfather".getBytes()); msg.setGroup("Movies"); rc = radio.send(msg, 0); assertThat(rc, is(true)); msg = dish.recv(0); assertThat(msg.getGroup(), is("Movies")); assertThat(new String(msg.data()), is("Godfather")); // Join group during connection rc = dish.join("TV"); assertThat(rc, is(true)); ZMQ.msleep(100); // This should arrive now as we joined the group msg = new Msg("Friends".getBytes()); msg.setGroup("TV"); rc = radio.send(msg, 0); assertThat(rc, is(true)); // Check the correct message arrived msg = dish.recv(0); assertThat(msg.getGroup(), is("TV")); assertThat(new String(msg.data()), is("Friends")); // Leaving group rc = dish.leave("TV"); assertThat(rc, is(true)); ZMQ.msleep(100); // This is not going to be sent as dish only subscribe to "Movies" msg = new Msg("Friends".getBytes()); msg.setGroup("TV"); rc = radio.send(msg, 0); assertThat(rc, is(true)); // This is going to be sent to the dish msg = new Msg("Godfather".getBytes()); msg.setGroup("Movies"); rc = radio.send(msg, 0); assertThat(rc, is(true)); msg = dish.recv(0); assertThat(msg.getGroup(), is("Movies")); assertThat(new String(msg.data()), is("Godfather")); ZMQ.close(dish); ZMQ.close(radio); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestRaw.java000066400000000000000000000173251455771126300205670ustar00rootroot00000000000000package zmq; import org.junit.Test; import java.nio.ByteBuffer; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestRaw { @Test public void testRaw2dealer() { final byte[] standardGreeting = new byte[64]; standardGreeting[0] = (byte) 0xff; standardGreeting[8] = 1; standardGreeting[9] = 0x7f; standardGreeting[10] = 3; standardGreeting[12] = 'N'; standardGreeting[13] = 'U'; standardGreeting[14] = 'L'; standardGreeting[15] = 'L'; String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // We'll be using this socket in raw mode SocketBase raw = ZMQ.socket(ctx, ZMQ.ZMQ_RAW); assertThat(raw, notNullValue()); boolean rc = ZMQ.setSocketOption(raw, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); rc = ZMQ.bind(raw, host); assertThat(rc, is(true)); host = (String) ZMQ.getSocketOptionExt(raw, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // We'll be using this socket as the other peer SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); rc = ZMQ.setSocketOption(dealer, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); rc = ZMQ.connect(dealer, host); assertThat(rc, is(true)); // Send a message on the dealer socket int ret = ZMQ.send(dealer, "Hello", 0); assertThat(ret, is(5)); // Connecting sends a zero message Msg zero = ZMQ.recv(raw, 0); assertThat(zero, notNullValue()); assertThat(zero.size(), is(0)); // Real data follows Msg greeting = ZMQ.recv(raw, 0); assertThat(greeting, notNullValue()); assertThat(greeting.size(), is(10)); assertThat(greeting.data(), is(new byte[] { (byte) 0xff, 0, 0, 0, 0, 0, 0, 0, 1, 0x7f })); // Send our own protocol greeting Msg ourGreeting = new Msg(standardGreeting); ourGreeting.setRoutingId(greeting.getRoutingId()); ret = ZMQ.send(raw, ourGreeting, 0); assertThat(ret, is(standardGreeting.length)); // Now we expect the data from the DEALER socket // We want the rest of greeting along with the Ready command int bytesRead = 0; ByteBuffer read = ByteBuffer.allocate(100); while (bytesRead < 97) { Msg msg = ZMQ.recv(raw, 0); assertThat(msg, notNullValue()); bytesRead += msg.size(); read.put(msg.buf()); } assertThat(read.get(0), is((byte) 3)); assertThat(read.get(1), is((byte) 0)); assertThat(read.get(2), is((byte) 'N')); assertThat(read.get(3), is((byte) 'U')); assertThat(read.get(4), is((byte) 'L')); assertThat(read.get(5), is((byte) 'L')); for (int idx = 0; idx < 16; ++idx) { assertThat(read.get(2 + 4 + idx), is((byte) 0)); } assertThat(read.get(54), is((byte) 4)); assertThat(read.get(55), is((byte) 051)); // octal notation, yes assertThat(read.get(56), is((byte) 5)); assertThat(read.get(57), is((byte) 'R')); assertThat(read.get(58), is((byte) 'E')); assertThat(read.get(59), is((byte) 'A')); assertThat(read.get(60), is((byte) 'D')); assertThat(read.get(61), is((byte) 'Y')); assertThat(read.get(62), is((byte) 013)); assertThat(read.get(63), is((byte) 'S')); assertThat(read.get(64), is((byte) 'o')); assertThat(read.get(65), is((byte) 'c')); assertThat(read.get(66), is((byte) 'k')); assertThat(read.get(67), is((byte) 'e')); assertThat(read.get(68), is((byte) 't')); assertThat(read.get(69), is((byte) '-')); assertThat(read.get(70), is((byte) 'T')); assertThat(read.get(71), is((byte) 'y')); assertThat(read.get(72), is((byte) 'p')); assertThat(read.get(73), is((byte) 'e')); assertThat(read.get(74), is((byte) 0)); assertThat(read.get(75), is((byte) 0)); assertThat(read.get(76), is((byte) 0)); assertThat(read.get(77), is((byte) 6)); assertThat(read.get(78), is((byte) 'D')); assertThat(read.get(79), is((byte) 'E')); assertThat(read.get(80), is((byte) 'A')); assertThat(read.get(81), is((byte) 'L')); assertThat(read.get(82), is((byte) 'E')); assertThat(read.get(83), is((byte) 'R')); assertThat(read.get(84), is((byte) 010)); assertThat(read.get(85), is((byte) 'I')); assertThat(read.get(86), is((byte) 'd')); assertThat(read.get(87), is((byte) 'e')); assertThat(read.get(88), is((byte) 'n')); assertThat(read.get(89), is((byte) 't')); assertThat(read.get(90), is((byte) 'i')); assertThat(read.get(91), is((byte) 't')); assertThat(read.get(92), is((byte) 'y')); assertThat(read.get(93), is((byte) 0)); assertThat(read.get(94), is((byte) 0)); assertThat(read.get(95), is((byte) 0)); assertThat(read.get(96), is((byte) 0)); // Send Ready command Msg ourReady = new Msg( new byte[] { 4, 41, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 6, 'R', 'O', 'U', 'T', 'E', 'R', 8, 'I', 'd', 'e', 'n', 't', 'i', 't', 'y', 0, 0, 0, 0 }); ourReady.setRoutingId(greeting.getRoutingId()); ZMQ.send(raw, ourReady, 0); // Now we expect the data from the DEALER socket Msg un = ZMQ.recv(raw, 0); assertThat(un, notNullValue()); assertThat(un.size(), is(7)); // Then we have a 5-byte message "Hello" assertThat(un.get(0), is((byte) 0)); assertThat(un.get(1), is((byte) 5)); assertThat(un.get(2), is((byte) 'H')); assertThat(un.get(3), is((byte) 'e')); assertThat(un.get(4), is((byte) 'l')); assertThat(un.get(5), is((byte) 'l')); assertThat(un.get(6), is((byte) 'o')); // Send "World" back to DEALER Msg backWorld = new Msg(new byte[] { 0, 5, 'W', 'o', 'r', 'l', 'd' }); backWorld.setRoutingId(greeting.getRoutingId()); ret = ZMQ.send(raw, backWorld, 0); assertThat(ret, is(7)); // Expect response on DEALER socket Msg recv = ZMQ.recv(dealer, 0); assertThat(recv.size(), is(5)); assertThat(recv.data(), is("World".getBytes(ZMQ.CHARSET))); ZMQ.close(raw); ZMQ.close(dealer); ZMQ.term(ctx); } @Test public void testRaw2Raw() { String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Set up listener STREAM. SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_RAW); assertThat(bind, notNullValue()); boolean rc = ZMQ.bind(bind, host); assertThat(rc, is(true)); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connection stream. SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_RAW); assertThat(connect, notNullValue()); // Do the connection. int peerRoutingId = ZMQ.connectPeer(connect, host); assertThat(peerRoutingId, not(0)); ZMQ.sleep(1); // Connecting sends a zero message Msg msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(0)); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestScatterGather.java000066400000000000000000000024231455771126300225670ustar00rootroot00000000000000package zmq; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestScatterGather { @Test public void testTcp() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase scatter = ZMQ.socket(ctx, ZMQ.ZMQ_SCATTER); assertThat(scatter, notNullValue()); boolean brc = ZMQ.bind(scatter, "tcp://127.0.0.1:*"); assertThat(brc, is(true)); String host = (String) ZMQ.getSocketOptionExt(scatter, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); SocketBase gather = ZMQ.socket(ctx, ZMQ.ZMQ_GATHER); assertThat(gather, notNullValue()); brc = ZMQ.connect(gather, host); assertThat(brc, is(true)); byte[] content = "12345678ABCDEFGH12345678abcdefgh".getBytes(ZMQ.CHARSET); // Send the message. int rc = ZMQ.send(scatter, content, 32, 0); assertThat(rc, is(32)); // Bounce the message back. Msg msg; msg = ZMQ.recv(gather, 0); assert (msg.size() == 32); // Tear down the wiring. ZMQ.close(scatter); ZMQ.close(gather); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestShutdownStress.java000066400000000000000000000030701455771126300230450ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class TestShutdownStress { private static final int THREAD_COUNT = 100; static class Worker implements Runnable { final SocketBase s; Worker(SocketBase s) { this.s = s; } @Override public void run() { boolean rc = ZMQ.connect(s, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); // Start closing the socket while the connecting process is underway. ZMQ.close(s); } } @Test public void testShutdownStress() throws Exception { Thread[] threads = new Thread[THREAD_COUNT]; for (int j = 0; j != 10; j++) { Ctx ctx = ZMQ.init(7); assertThat(ctx, notNullValue()); SocketBase s1 = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(s1, notNullValue()); boolean rc = ZMQ.bind(s1, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); for (int i = 0; i != THREAD_COUNT; i++) { SocketBase s2 = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assert (s2 != null); threads[i] = new Thread(new Worker(s2)); threads[i].start(); } for (int i = 0; i != THREAD_COUNT; i++) { threads[i].join(); } ZMQ.close(s1); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/TestSocketPoll.java000066400000000000000000000062651455771126300221160ustar00rootroot00000000000000package zmq; import org.junit.Test; import zmq.util.Utils; import java.util.concurrent.atomic.AtomicBoolean; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class TestSocketPoll { @Test public void testPollIn() throws Exception { System.out.println("Scenario 1"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // bind server boolean rc = ZMQ.bind(server, "tcp://*:" + port); assertThat(rc, is(true)); // create a client SocketBase client = ZMQ.socket(context, ZMQ.ZMQ_CLIENT); assertThat(client, notNullValue()); rc = ZMQ.connect(client, "tcp://localhost:" + port); assertThat(rc, is(true)); // poll no message int ready = server.poll(ZMQ.ZMQ_POLLIN, 10, null); assertThat(ready, is(-1)); assertThat(server.errno(), is(ZError.EAGAIN)); // Send a message from client Msg msg = new Msg("X".getBytes()); int size = ZMQ.send(client, msg, 0); assertThat(size, is(1)); ready = server.poll(ZMQ.ZMQ_POLLIN, 10, null); assertThat(ready, is(ZMQ.ZMQ_POLLIN)); ZMQ.close(client); ZMQ.close(server); ZMQ.term(context); } @Test public void cancelPoll() throws Exception { System.out.println("Scenario 2"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); AtomicBoolean cancellationToken = new AtomicBoolean(false); Thread t = new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } server.cancel(cancellationToken); }); t.start(); // poll canceled int ready = server.poll(ZMQ.ZMQ_POLLIN, -1, cancellationToken); assertThat(ready, is(-1)); assertThat(server.errno(), is(ZError.ECANCELED)); ZMQ.close(server); ZMQ.term(context); } @Test public void testZeroTimeout() throws Exception { System.out.println("Scenario 3"); int port = Utils.findOpenPort(); Ctx context = ZMQ.createContext(); assertThat(context, notNullValue()); // Create a server SocketBase server = ZMQ.socket(context, ZMQ.ZMQ_SERVER); assertThat(server, notNullValue()); // bind server boolean rc = ZMQ.bind(server, "tcp://*:" + port); assertThat(rc, is(true)); // poll zero timeout int ready = server.poll(ZMQ.ZMQ_POLLIN, 0, null); assertThat(ready, is(-1)); assertThat(server.errno(), is(ZError.EAGAIN)); ZMQ.close(server); ZMQ.term(context); } } jeromq-0.6.0/src/test/java/zmq/TestTermEndpoint.java000066400000000000000000000156031455771126300224430ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.util.Utils; public class TestTermEndpoint { protected String endpointWildcard() { return "tcp://127.0.0.1:" + '*'; } protected String endpointNormal() throws IOException { int port = Utils.findOpenPort(); return "tcp://127.0.0.1:" + port; } @Test(timeout = 5000) public void testUnbindWildcard() { String ep = endpointWildcard(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.bind(push, ep); assertThat(rc, is(true)); ep = (String) ZMQ.getSocketOptionExt(push, ZMQ.ZMQ_LAST_ENDPOINT); SocketBase pull = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); rc = ZMQ.connect(pull, ep); assertThat(rc, is(true)); // Pass one message through to ensure the connection is established. int r = ZMQ.send(push, "ABC", 0); assertThat(r, is(3)); Msg msg = ZMQ.recv(pull, 0); assertThat(msg.size(), is(3)); // Unbind the lisnening endpoint rc = ZMQ.unbind(push, ep); assertThat(rc, is(true)); // Check that sending would block (there's no outbound connection). // There's no way to do this except with a sleep and a loop while (ZMQ.send(push, "ABC", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Clean up. ZMQ.close(pull); ZMQ.close(push); ZMQ.term(ctx); } @Test(timeout = 5000) public void testDisconnectWildcard() { String ep = endpointWildcard(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.bind(push, ep); assertThat(rc, is(true)); ep = (String) ZMQ.getSocketOptionExt(push, ZMQ.ZMQ_LAST_ENDPOINT); SocketBase pull = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); rc = ZMQ.connect(pull, ep); assertThat(rc, is(true)); // Pass one message through to ensure the connection is established. int r = ZMQ.send(push, "ABC", 0); assertThat(r, is(3)); Msg msg = ZMQ.recv(pull, 0); assertThat(msg.size(), is(3)); // Disconnect the bound endpoint rc = ZMQ.disconnect(push, ep); assertThat(rc, is(true)); // Check that sending would block (there's no outbound connection). // There's no way to do this except with a sleep and a loop while (ZMQ.send(push, "ABC", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Clean up. ZMQ.close(pull); ZMQ.close(push); ZMQ.term(ctx); } @Test public void testUnbindWildcardByWildcard() { String ep = endpointWildcard(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.bind(push, ep); assertThat(rc, is(true)); // Unbind the lisnening endpoint rc = ZMQ.unbind(push, ep); assertThat(rc, is(false)); assertThat(push.errno.get(), is(ZError.ENOENT)); // Clean up. ZMQ.close(push); ZMQ.term(ctx); } @Test public void testDisconnectWildcardByWildcard() { String ep = endpointWildcard(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean rc = ZMQ.bind(push, ep); assertThat(rc, is(true)); // Disconnect the bound endpoint rc = ZMQ.disconnect(push, ep); assertThat(rc, is(false)); assertThat(push.errno.get(), is(ZError.ENOENT)); // Clean up. ZMQ.close(push); ZMQ.term(ctx); } @Test(timeout = 5000) public void testUnbind() throws Exception { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); String ep; boolean rc; do { ep = endpointNormal(); // we might have to repeat until we find an open port rc = ZMQ.bind(push, ep); } while (!rc && push.errno() == ZError.EADDRINUSE); SocketBase pull = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); rc = ZMQ.connect(pull, ep); assertThat(rc, is(true)); // Pass one message through to ensure the connection is established. int r = ZMQ.send(push, "ABC", 0); assertThat(r, is(3)); Msg msg = ZMQ.recv(pull, 0); assertThat(msg.size(), is(3)); // Unbind the lisnening endpoint rc = ZMQ.unbind(push, ep); assertThat(rc, is(true)); // Check that sending would block (there's no outbound connection). // There's no way to do this except with a sleep and a loop while (ZMQ.send(push, "ABC", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Clean up. ZMQ.close(pull); ZMQ.close(push); ZMQ.term(ctx); } @Test(timeout = 5000) public void testDisconnect() throws Exception { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); String ep; boolean rc; do { ep = endpointNormal(); // we might have to repeat until we find an open port rc = ZMQ.bind(push, ep); } while (!rc && push.errno() == ZError.EADDRINUSE); SocketBase pull = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); rc = ZMQ.connect(pull, ep); assertThat(rc, is(true)); // Pass one message through to ensure the connection is established. int r = ZMQ.send(push, "ABC", 0); assertThat(r, is(3)); Msg msg = ZMQ.recv(pull, 0); assertThat(msg.size(), is(3)); // Disconnect the bound endpoint rc = ZMQ.disconnect(push, ep); assertThat(rc, is(true)); // Check that sending would block (there's no outbound connection). // There's no way to do this except with a sleep and a loop while (ZMQ.send(push, "ABC", ZMQ.ZMQ_DONTWAIT) != -1) { ZMQ.sleep(2); } // Clean up. ZMQ.close(pull); ZMQ.close(push); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/TestTimeo.java000066400000000000000000000053411455771126300211060ustar00rootroot00000000000000package zmq; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; public class TestTimeo { static class Worker implements Runnable { final Ctx ctx; Worker(Ctx ctx) { this.ctx = ctx; } @Override public void run() { ZMQ.sleep(1); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(sc, notNullValue()); boolean rc = ZMQ.connect(sc, "inproc://timeout_test"); assertThat(rc, is(true)); ZMQ.sleep(1); ZMQ.close(sc); } } @Test public void testTimeo() throws Exception { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(sb, notNullValue()); boolean rc = ZMQ.bind(sb, "inproc://timeout_test"); assertThat(rc, is(true)); // Check whether non-blocking recv returns immediately. Msg msg = ZMQ.recv(sb, ZMQ.ZMQ_DONTWAIT); assertThat(msg, nullValue()); // Check whether recv timeout is honoured. int timeout = 500; ZMQ.setSocketOption(sb, ZMQ.ZMQ_RCVTIMEO, timeout); long watch = ZMQ.startStopwatch(); msg = ZMQ.recv(sb, 0); assertThat(msg, nullValue()); long elapsed = ZMQ.stopStopwatch(watch); assertThat(elapsed > 440000 && elapsed < 550000, is(true)); // Check whether connection during the wait doesn't distort the timeout. timeout = 2000; ZMQ.setSocketOption(sb, ZMQ.ZMQ_RCVTIMEO, timeout); Thread thread = new Thread(new Worker(ctx)); thread.start(); watch = ZMQ.startStopwatch(); msg = ZMQ.recv(sb, 0); assertThat(msg, nullValue()); elapsed = ZMQ.stopStopwatch(watch); assertThat(elapsed > 1900000 && elapsed < 2100000, is(true)); thread.join(); // Check that timeouts don't break normal message transfer. SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(sc, notNullValue()); ZMQ.setSocketOption(sb, ZMQ.ZMQ_RCVTIMEO, timeout); ZMQ.setSocketOption(sb, ZMQ.ZMQ_SNDTIMEO, timeout); rc = ZMQ.connect(sc, "inproc://timeout_test"); assertThat(rc, is(true)); Msg smsg = new Msg("12345678ABCDEFGH12345678abcdefgh".getBytes(ZMQ.CHARSET)); int r = ZMQ.send(sc, smsg, 0); assertThat(r, is(32)); msg = ZMQ.recv(sb, 0); assertThat(msg.size(), is(32)); ZMQ.close(sc); ZMQ.close(sb); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/io/000077500000000000000000000000001455771126300167325ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/io/AbstractProtocolVersion.java000066400000000000000000000075021455771126300244340ustar00rootroot00000000000000package zmq.io; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.ZMQ.Event; import zmq.util.TestUtils; public abstract class AbstractProtocolVersion { protected static final int REPETITIONS = 1000; static class SocketMonitor extends Thread { private final Ctx ctx; private final String monitorAddr; private final ZMQ.Event[] events = new ZMQ.Event[1]; public SocketMonitor(Ctx ctx, String monitorAddr) { this.ctx = ctx; this.monitorAddr = monitorAddr; } @Override public void run() { SocketBase s = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); boolean rc = s.connect(monitorAddr); assertThat(rc, is(true)); // Only some of the exceptional events could fire ZMQ.Event event = ZMQ.Event.read(s); if (event == null && s.errno() == ZError.ETERM) { s.close(); return; } assertThat(event, notNullValue()); events[0] = event; s.close(); } } protected byte[] assertProtocolVersion(int version, List raws, String payload) throws IOException, InterruptedException { String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase receiver = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(receiver, notNullValue()); boolean rc = ZMQ.setSocketOption(receiver, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); rc = ZMQ.monitorSocket(receiver, "inproc://monitor", ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL); assertThat(rc, is(true)); SocketMonitor monitor = new SocketMonitor(ctx, "inproc://monitor"); monitor.start(); rc = ZMQ.bind(receiver, host); assertThat(rc, is(true)); String ep = (String) ZMQ.getSocketOptionExt(receiver, ZMQ.ZMQ_LAST_ENDPOINT); int port = TestUtils.port(ep); Socket sender = new Socket("127.0.0.1", port); OutputStream out = sender.getOutputStream(); for (ByteBuffer raw : raws) { out.write(raw.array()); } Msg msg = ZMQ.recv(receiver, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data(), ZMQ.CHARSET), is(payload)); monitor.join(); final Event event = monitor.events[0]; assertThat(event, notNullValue()); assertThat(event.event, is(ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL)); assertThat((Integer) event.arg, is(version)); InputStream in = sender.getInputStream(); byte[] data = new byte[255]; int read = in.read(data); sender.close(); ZMQ.close(receiver); ZMQ.term(ctx); return Arrays.copyOf(data, read); } protected List raws(int revision) { List raws = new ArrayList<>(); ByteBuffer raw = ByteBuffer.allocate(12); // send V1 header raw.put((byte) 0xff).put(new byte[8]).put((byte) 0x1); // protocol revision raw.put((byte) revision); // socket type raw.put((byte) ZMQ.ZMQ_PUSH); raws.add(raw); return raws; } protected ByteBuffer identity() { return ByteBuffer.allocate(2) // size .put((byte) 1) // flags .put((byte) 0); } } jeromq-0.6.0/src/test/java/zmq/io/MetadataTest.java000066400000000000000000000140241455771126300221560ustar00rootroot00000000000000package zmq.io; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class MetadataTest { private static class ZapHandler implements Runnable { private final SocketBase handler; public ZapHandler(SocketBase handler) { this.handler = handler; } @SuppressWarnings("unused") @Override public void run() { byte[] metadata = { 5, 'H', 'e', 'l', 'l', 'o', 0, 0, 0, 5, 'W', 'o', 'r', 'l', 'd' }; // Process ZAP requests forever while (true) { Msg version = ZMQ.recv(handler, 0); if (version == null) { break; // Terminating } Msg sequence = ZMQ.recv(handler, 0); Msg domain = ZMQ.recv(handler, 0); Msg address = ZMQ.recv(handler, 0); Msg identity = ZMQ.recv(handler, 0); Msg mechanism = ZMQ.recv(handler, 0); assertThat(new String(version.data(), ZMQ.CHARSET), is("1.0")); assertThat(new String(mechanism.data(), ZMQ.CHARSET), is("NULL")); int ret = ZMQ.send(handler, version, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(3)); ret = ZMQ.send(handler, sequence, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); System.out.println("Sending ZAP reply"); if ("DOMAIN".equals(new String(domain.data(), ZMQ.CHARSET))) { ret = ZMQ.send(handler, "200", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(3)); ret = ZMQ.send(handler, "OK", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(2)); ret = ZMQ.send(handler, "anonymous", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(9)); ret = ZMQ.send(handler, metadata, metadata.length, 0); assertThat(ret, is(metadata.length)); } else { ret = ZMQ.send(handler, "400", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(3)); ret = ZMQ.send(handler, "BAD DOMAIN", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(10)); ret = ZMQ.send(handler, "", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(0)); ret = ZMQ.send(handler, "", 0); assertThat(ret, is(0)); } } ZMQ.closeZeroLinger(handler); } } @Test public void testMetadata() throws IOException, InterruptedException { int port = Utils.findOpenPort(); String host = "tcp://127.0.0.1:" + port; Ctx ctx = ZMQ.createContext(); // Spawn ZAP handler // We create and bind ZAP socket in main thread to avoid case // where child thread does not start up fast enough. SocketBase handler = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(handler, notNullValue()); boolean rc = ZMQ.bind(handler, "inproc://zeromq.zap.01"); assertThat(rc, is(true)); Thread thread = new Thread(new ZapHandler(handler)); thread.start(); // Server socket will accept connections SocketBase server = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(server, notNullValue()); SocketBase client = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(client, notNullValue()); ZMQ.setSocketOption(server, ZMQ.ZMQ_ZAP_DOMAIN, "DOMAIN"); ZMQ.setSocketOption(server, ZMQ.ZMQ_SELFADDR_PROPERTY_NAME, "X-Local-Address"); rc = ZMQ.bind(server, host); assertThat(rc, is(true)); rc = ZMQ.connect(client, host); assertThat(rc, is(true)); int ret = ZMQ.send(client, "This is a message", 0); assertThat(ret, is(17)); Msg msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); String prop = ZMQ.getMessageMetadata(msg, "Socket-Type"); assertThat(prop, is("DEALER")); prop = ZMQ.getMessageMetadata(msg, "User-Id"); assertThat(prop, is("anonymous")); prop = ZMQ.getMessageMetadata(msg, "Peer-Address"); assertThat(prop.startsWith("127.0.0.1:"), is(true)); prop = ZMQ.getMessageMetadata(msg, "no such"); assertThat(prop, nullValue()); prop = ZMQ.getMessageMetadata(msg, "Hello"); assertThat(prop, is("World")); prop = ZMQ.getMessageMetadata(msg, "X-Local-Address"); assertThat(prop, is("127.0.0.1:" + port)); ZMQ.closeZeroLinger(server); ZMQ.closeZeroLinger(client); // Shutdown ZMQ.term(ctx); // Wait until ZAP handler terminates thread.join(); } @Test public void testWriteRead() throws IOException { Map srcMap = new HashMap<>(); srcMap.put("keyEmpty", ""); srcMap.put("keyNull", null); Metadata fromMap = new Metadata(srcMap); assertThat(fromMap.get("keyEmpty"), is("")); assertThat(fromMap.get("keyNull"), is("")); Metadata src = new Metadata(); src.put("key", "value"); src.put("keyEmpty", ""); src.put("keyNull", null); src.put(UUID.randomUUID().toString(), UUID.randomUUID().toString()); ByteArrayOutputStream stream = new ByteArrayOutputStream(); src.write(stream); byte[] array = stream.toByteArray(); Metadata dst = new Metadata(); dst.read(ByteBuffer.wrap(array), 0, null); assertThat(dst, is(src)); assertThat(dst.get("keyEmpty"), is("")); assertThat(dst.get("keyNull"), is("")); } } jeromq-0.6.0/src/test/java/zmq/io/MsgsTest.java000066400000000000000000000022741455771126300213530ustar00rootroot00000000000000package zmq.io; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Test; import org.zeromq.ZMQ; import zmq.Msg; public class MsgsTest { @Test public void testPutMax() { assertPutStartsWith(255); } @Test public void testPutMiddle() { assertPutStartsWith(127); } @Test public void testPutMiddlePlusOne() { assertPutStartsWith(128); } @Test public void testPutZero() { assertPutStartsWith(0); } @Test public void testPutEncodesWithLength() { String test = "test1"; Msg msg = new Msg(test.length() + 1); msg.putShortString(test); String read = new String(msg.data(), ZMQ.CHARSET); assertThat(read, is("\5test1")); } private void assertPutStartsWith(int max) { Msg msg = new Msg(max + 1); byte[] bytes = new byte[max]; Arrays.fill(bytes, (byte) 'a'); String string = new String(bytes); msg.putShortString(string); boolean rc = Msgs.startsWith(msg, string, true); assertThat(rc, is(true)); } } jeromq-0.6.0/src/test/java/zmq/io/StreamEngineTest.java000066400000000000000000000044021455771126300230160ustar00rootroot00000000000000package zmq.io; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Config; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class StreamEngineTest { @Test public void testEncoderFlipIssue520() { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase sender = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(sender, notNullValue()); boolean rc = ZMQ.setSocketOption(sender, ZMQ.ZMQ_IMMEDIATE, false); assertThat(rc, is(true)); SocketBase receiver = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(receiver, notNullValue()); String addr = "tcp://localhost:*"; rc = ZMQ.bind(receiver, addr); assertThat(rc, is(true)); addr = (String) ZMQ.getSocketOptionExt(receiver, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(addr, notNullValue()); rc = ZMQ.connect(sender, addr); assertThat(rc, is(true)); final int headerSize = 8 + 1; // first message + second message header fill the buffer byte[] msg1 = msg(Config.OUT_BATCH_SIZE.getValue() - 2 * headerSize); // second message will go in zero-copy mode byte[] msg2 = msg(Config.OUT_BATCH_SIZE.getValue()); exchange(sender, receiver, msg1, msg2, msg1, msg2); ZMQ.close(receiver); ZMQ.close(sender); ZMQ.term(ctx); } private byte[] msg(int length) { byte[] msg = new byte[length]; for (int idx = 0; idx < msg.length; ++idx) { msg[idx] = (byte) idx; } return msg; } private void exchange(SocketBase sender, SocketBase receiver, byte[]... msgs) { int repetition = 50; for (int idx = 0; idx < repetition; ++idx) { for (byte[] msg : msgs) { int num = ZMQ.send(sender, msg, 0); assertThat(num, is(msg.length)); } } for (int idx = 0; idx < repetition; ++idx) { for (byte[] msg : msgs) { Msg received = ZMQ.recv(receiver, 0); assertThat(received.data(), is(msg)); } } } } jeromq-0.6.0/src/test/java/zmq/io/TimerEventTest.java000066400000000000000000000060001455771126300225130ustar00rootroot00000000000000package zmq.io; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.util.TestUtils; public class TimerEventTest { static class SocketMonitor extends Thread { private final Ctx ctx; private final String monitorAddr; private final AtomicReference event = new AtomicReference<>(); public SocketMonitor(Ctx ctx, String monitorAddr) { this.ctx = ctx; this.monitorAddr = monitorAddr; } @Override public void run() { SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(monitor, notNullValue()); boolean rc = monitor.connect(monitorAddr); assertThat(rc, is(true)); ZMQ.Event event = ZMQ.Event.read(monitor); if (event == null && monitor.errno() == ZError.ETERM) { monitor.close(); return; } assertThat(event, notNullValue()); this.event.set(event); monitor.close(); } } private byte[] incompleteHandshake() { byte[] raw = new byte[10]; raw[0] = (byte) 0xff; raw[9] = (byte) 0x1; return raw; } @Test public void testHandshakeTimeout() throws IOException, InterruptedException { int handshakeInterval = 10; Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase socket = ctx.createSocket(ZMQ.ZMQ_REP); assertThat(socket, notNullValue()); boolean rc = ZMQ.setSocketOption(socket, ZMQ.ZMQ_HANDSHAKE_IVL, handshakeInterval); assertThat(rc, is(true)); rc = ZMQ.monitorSocket(socket, "inproc://monitor", ZMQ.ZMQ_EVENT_DISCONNECTED); assertThat(rc, is(true)); SocketMonitor monitor = new SocketMonitor(ctx, "inproc://monitor"); monitor.start(); rc = ZMQ.bind(socket, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); String endpoint = (String) ZMQ.getSocketOptionExt(socket, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); Socket sender = new Socket("127.0.0.1", TestUtils.port(endpoint)); OutputStream out = sender.getOutputStream(); out.write(incompleteHandshake()); out.flush(); monitor.join(); // there shall be a disconnected event because of the handshake timeout final ZMQ.Event event = monitor.event.get(); assertThat(event, notNullValue()); assertThat(event.event, is(ZMQ.ZMQ_EVENT_DISCONNECTED)); out.close(); sender.close(); ZMQ.close(socket); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/io/V0ProtocolTest.java000066400000000000000000000036561455771126300224560ustar00rootroot00000000000000package zmq.io; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; import org.junit.Test; import zmq.ZMQ; public class V0ProtocolTest extends AbstractProtocolVersion { @Test public void testFixIssue524() throws IOException, InterruptedException { for (int idx = 0; idx < REPETITIONS; ++idx) { if (idx % 100 == 0) { System.out.print(idx + " "); } testProtocolVersion0short(); } System.out.println(); } @Test(timeout = 2000) public void testProtocolVersion0short() throws IOException, InterruptedException { ByteBuffer raw = ByteBuffer.allocate(11) // send unversioned identity message // size .put((byte) 0x01) // flags .put((byte) 0); // and payload raw.put((byte) 8).put((byte) 0).put("abcdefg".getBytes(ZMQ.CHARSET)); assertProtocolVersion(0, raw, "abcdefg"); } @Test(timeout = 2000) public void testProtocolVersion0long() throws IOException, InterruptedException { ByteBuffer raw = ByteBuffer.allocate(35) // send unversioned identity message // large message indicator .put((byte) 0xff) // size .put(new byte[7]).put((byte) 9) // flags .put((byte) 0) // identity .put("identity".getBytes(ZMQ.CHARSET)); // and payload raw.put((byte) 0xff).put(new byte[7]).put((byte) 8).put((byte) 0).put("abcdefg".getBytes(ZMQ.CHARSET)); assertProtocolVersion(0, raw, "abcdefg"); } private byte[] assertProtocolVersion(int version, ByteBuffer raw, String payload) throws IOException, InterruptedException { return assertProtocolVersion(version, Collections.singletonList(raw), payload); } } jeromq-0.6.0/src/test/java/zmq/io/V1ProtocolTest.java000066400000000000000000000025251455771126300224510ustar00rootroot00000000000000package zmq.io; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.junit.Test; import zmq.ZMQ; public class V1ProtocolTest extends AbstractProtocolVersion { @Test public void testFixIssue524() throws IOException, InterruptedException { for (int idx = 0; idx < REPETITIONS; ++idx) { if (idx % 100 == 0) { System.out.print(idx + " "); } testProtocolVersion1short(); } System.out.println(); } @Test public void testProtocolVersion1short() throws IOException, InterruptedException { List raws = raws(0); raws.add(identity()); // payload ByteBuffer raw = ByteBuffer.allocate(9); raw.put((byte) 8).put((byte) 0).put("abcdefg".getBytes(ZMQ.CHARSET)); raws.add(raw); assertProtocolVersion(1, raws, "abcdefg"); } @Test public void testProtocolVersion1long() throws IOException, InterruptedException { List raws = raws(0); raws.add(identity()); // payload ByteBuffer raw = ByteBuffer.allocate(17); raw.put((byte) 0xff).put(new byte[7]).put((byte) 8).put((byte) 0).put("abcdefg".getBytes(ZMQ.CHARSET)); raws.add(raw); assertProtocolVersion(1, raws, "abcdefg"); } } jeromq-0.6.0/src/test/java/zmq/io/V2ProtocolTest.java000066400000000000000000000032761455771126300224560ustar00rootroot00000000000000package zmq.io; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.junit.Test; import zmq.ZMQ; import zmq.util.Wire; public class V2ProtocolTest extends AbstractProtocolVersion { @Override protected ByteBuffer identity() { return ByteBuffer.allocate(2) // flag .put((byte) 0) // length .put((byte) 0); } @Test public void testFixIssue524() throws IOException, InterruptedException { for (int idx = 0; idx < REPETITIONS; ++idx) { if (idx % 100 == 0) { System.out.print(idx + " "); } testProtocolVersion2short(); } System.out.println(); } @Test public void testProtocolVersion2short() throws IOException, InterruptedException { List raws = raws(1); raws.add(identity()); ByteBuffer raw = ByteBuffer.allocate(9) // flag .put((byte) 0) // length .put((byte) 7) // payload .put("abcdefg".getBytes(ZMQ.CHARSET)); raws.add(raw); assertProtocolVersion(2, raws, "abcdefg"); } @Test public void testProtocolVersion2long() throws IOException, InterruptedException { List raws = raws(1); raws.add(identity()); ByteBuffer raw = ByteBuffer.allocate(17); // flag raw.put((byte) 2); // length Wire.putUInt64(raw, 8); // payload raw.put("abcdefgh".getBytes(ZMQ.CHARSET)); raws.add(raw); assertProtocolVersion(2, raws, "abcdefgh"); } } jeromq-0.6.0/src/test/java/zmq/io/coder/000077500000000000000000000000001455771126300200265ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/io/coder/AbstractDecoderTest.java000066400000000000000000000100761455771126300245660ustar00rootroot00000000000000package zmq.io.coder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Msg; import zmq.ZMQ; import zmq.io.coder.IDecoder.Step; import zmq.util.ValueReference; public abstract class AbstractDecoderTest { DecoderBase decoder; // as if it read data from socket abstract int readShortMessage(ByteBuffer buf); // as if it read data from socket abstract int readLongMessage1(ByteBuffer buf); abstract int readLongMessage2(ByteBuffer buf); abstract int readExtraLongMessage(ByteBuffer buf); @Test public void testReader() { ByteBuffer in = decoder.getBuffer(); int insize = readShortMessage(in); assertThat(insize, is(7)); in.flip(); ValueReference process = new ValueReference<>(0); decoder.decode(in, insize, process); assertThat(process.get(), is(7)); Msg msg = decoder.msg(); assertThat(msg, notNullValue()); assertThat(msg.flags(), is(1)); } @Test public void testReaderLong() { ByteBuffer in = decoder.getBuffer(); int insize = readLongMessage1(in); assertThat(insize, is(64)); in.flip(); ValueReference process = new ValueReference<>(0); decoder.decode(in, insize, process); assertThat(process.get(), is(64)); in = decoder.getBuffer(); assertThat(in.capacity(), is(200)); assertThat(in.position(), is(62)); in.put("23456789".getBytes(ZMQ.CHARSET)); insize = readLongMessage2(in); assertThat(insize, is(200)); decoder.decode(in, 138, process); assertThat(process.get(), is(138)); assertThat(in.array()[199], is((byte) 'x')); Msg msg = decoder.msg(); assertThat(msg, notNullValue()); byte last = msg.data()[199]; assertThat(last, is((byte) 'x')); assertThat(msg.size(), is(200)); assertThat(msg.flags(), is(1)); } @Test public void testReaderExtraLong() { ByteBuffer in = decoder.getBuffer(); int insize = readExtraLongMessage(in); // assertThat(insize, is(62)); in.flip(); ValueReference process = new ValueReference<>(0); decoder.decode(in, insize, process); assertThat(process.get(), is(insize)); in = decoder.getBuffer(); assertThat(in.capacity(), is(330)); assertThat(in.position(), is(52)); in.put("23456789".getBytes(ZMQ.CHARSET)); in.put("0123456789".getBytes(ZMQ.CHARSET)); insize = readLongMessage2(in); assertThat(insize, is(200)); insize = readLongMessage2(in); assertThat(insize, is(330)); decoder.decode(in, 278, process); assertThat(process.get(), is(278)); assertThat(in.array()[329], is((byte) 'x')); Msg msg = decoder.msg(); assertThat(msg, notNullValue()); byte last = msg.data()[329]; assertThat(last, is((byte) 'x')); assertThat(msg.size(), is(330)); } @Test public void testReaderMultipleMsg() { ByteBuffer in = decoder.getBuffer(); int insize = readShortMessage(in); assertThat(insize, is(7)); readShortMessage(in); in.flip(); ValueReference processed = new ValueReference<>(0); decoder.decode(in, 14, processed); assertThat(processed.get(), is(7)); assertThat(in.position(), is(7)); assertThat(decoder.msg(), notNullValue()); Step.Result result = decoder.decode(in, 6, processed); assertThat(processed.get(), is(6)); assertThat(in.position(), is(13)); assertThat(result, is(Step.Result.MORE_DATA)); result = decoder.decode(in, 10, processed); assertThat(processed.get(), is(1)); assertThat(in.position(), is(14)); assertThat(result, is(Step.Result.DECODED)); assertThat(decoder.msg(), notNullValue()); } } jeromq-0.6.0/src/test/java/zmq/io/coder/CustomDecoderTest.java000066400000000000000000000064611455771126300243000ustar00rootroot00000000000000package zmq.io.coder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.io.coder.IDecoder.Step; import zmq.msg.MsgAllocatorThreshold; import zmq.util.Errno; import zmq.util.ValueReference; public class CustomDecoderTest { static class CustomDecoder extends Decoder { private final Step readHeader = this::readHeader; private final Step readBody = this::readBody; final ByteBuffer header = ByteBuffer.allocate(10); Msg msg; int size = -1; public CustomDecoder(int bufsize, long maxmsgsize) { super(new Errno(), bufsize, maxmsgsize, new MsgAllocatorThreshold()); nextStep(header, readHeader); } private Step.Result readHeader() { byte[] headerBuff = new byte[6]; header.position(0); header.get(headerBuff); assertThat(new String(headerBuff, 0, 6, ZMQ.CHARSET), is("HEADER")); ByteBuffer b = header.duplicate(); b.position(6); size = b.getInt(); msg = allocate(size); nextStep(msg, readBody); return Step.Result.MORE_DATA; } private Step.Result readBody() { nextStep(header, readHeader); return Step.Result.DECODED; } } @Test public void testCustomDecoder() { CustomDecoder cdecoder = new CustomDecoder(32, 64); ByteBuffer in = cdecoder.getBuffer(); int insize = readHeader(in); assertThat(insize, is(10)); readBody(in); in.flip(); ValueReference processed = new ValueReference<>(0); Step.Result result = cdecoder.decode(in, 30, processed); assertThat(processed.get(), is(30)); assertThat(cdecoder.size, is(20)); assertThat(result, is(Step.Result.DECODED)); } private void readBody(ByteBuffer in) { in.put("1234567890".getBytes(ZMQ.CHARSET)); in.put("1234567890".getBytes(ZMQ.CHARSET)); } private int readHeader(ByteBuffer in) { in.put("HEADER".getBytes(ZMQ.CHARSET)); in.putInt(20); return in.position(); } @SuppressWarnings("deprecation") @Test public void testAssignCustomDecoder() { Ctx ctx = ZMQ.createContext(); SocketBase socket = ctx.createSocket(ZMQ.ZMQ_PAIR); boolean rc = socket.setSocketOpt(ZMQ.ZMQ_DECODER, CustomDecoder.class); assertThat(rc, is(true)); ZMQ.close(socket); ZMQ.term(ctx); } private static class WrongDecoder extends CustomDecoder { public WrongDecoder(int bufsize) { super(bufsize, 0); } } @SuppressWarnings("deprecation") @Test(expected = ZError.InstantiationException.class) public void testAssignWrongCustomDecoder() { Ctx ctx = ZMQ.createContext(); SocketBase socket = ctx.createSocket(ZMQ.ZMQ_PAIR); try { socket.setSocketOpt(ZMQ.ZMQ_DECODER, WrongDecoder.class); } finally { ZMQ.close(socket); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/io/coder/CustomEncoderTest.java000066400000000000000000000061311455771126300243040ustar00rootroot00000000000000package zmq.io.coder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.util.Errno; import zmq.util.ValueReference; public class CustomEncoderTest { private final Helper.DummySocketChannel sock = new Helper.DummySocketChannel(); private int write(ByteBuffer out) { return sock.write(out); } static class CustomEncoder extends EncoderBase { public static final boolean RAW_ENCODER = true; private final Runnable readHeader = this::readHeader; private final Runnable readBody = this::readBody; final ByteBuffer header = ByteBuffer.allocate(10); public CustomEncoder(int bufsize, long maxmsgsize) { super(new Errno(), bufsize); initStep(readBody, true); } private void readHeader() { nextStep(inProgress, readBody, !inProgress.hasMore()); } private void readBody() { if (inProgress == null) { return; } header.clear(); header.put("HEADER".getBytes(ZMQ.CHARSET)); header.putInt(inProgress.size()); header.flip(); nextStep(header, header.limit(), readHeader, false); } } @Test public void testCustomEncoder() { CustomEncoder cencoder = new CustomEncoder(32, Integer.MAX_VALUE / 2); Msg msg = new Msg("12345678901234567890".getBytes(ZMQ.CHARSET)); cencoder.loadMsg(msg); ValueReference ref = new ValueReference<>(); int outsize = cencoder.encode(ref, 0); assertThat(outsize, is(30)); ByteBuffer out = ref.get(); out.flip(); write(out); byte[] data = sock.data(); assertThat(new String(data, 0, 6, ZMQ.CHARSET), is("HEADER")); assertThat((int) data[9], is(20)); assertThat(new String(data, 10, 20, ZMQ.CHARSET), is("12345678901234567890")); } @SuppressWarnings("deprecation") @Test public void testAssignCustomEncoder() { Ctx ctx = ZMQ.createContext(); SocketBase socket = ctx.createSocket(ZMQ.ZMQ_PAIR); boolean rc = socket.setSocketOpt(ZMQ.ZMQ_ENCODER, CustomEncoder.class); assertThat(rc, is(true)); ZMQ.close(socket); ZMQ.term(ctx); } private static class WrongEncoder extends CustomEncoder { public WrongEncoder(int bufsize) { super(bufsize, 0); } } @SuppressWarnings("deprecation") @Test(expected = ZError.InstantiationException.class) public void testAssignWrongCustomEncoder() { Ctx ctx = ZMQ.createContext(); SocketBase socket = ctx.createSocket(ZMQ.ZMQ_PAIR); try { socket.setSocketOpt(ZMQ.ZMQ_ENCODER, WrongEncoder.class); } finally { ZMQ.close(socket); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/io/coder/V1DecoderTest.java000066400000000000000000000031101455771126300233000ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import org.junit.Before; import zmq.ZMQ; import zmq.io.coder.v1.V1Decoder; import zmq.msg.MsgAllocatorThreshold; import zmq.util.Errno; import zmq.util.Wire; public class V1DecoderTest extends AbstractDecoderTest { @Before public void setUp() { decoder = new V1Decoder(new Errno(), 64, 512, new MsgAllocatorThreshold()); } // as if it read data from socket @Override int readShortMessage(ByteBuffer buf) { buf.put((byte) 6); buf.put((byte) 1); // flag buf.put("hello".getBytes(ZMQ.CHARSET)); return buf.position(); } // as if it read data from socket @Override int readLongMessage1(ByteBuffer buf) { buf.put((byte) 201); buf.put((byte) 3); // flag for (int i = 0; i < 6; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put("01".getBytes(ZMQ.CHARSET)); return buf.position(); } @Override int readLongMessage2(ByteBuffer buf) { for (int i = 0; i < 13; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put(buf.position() - 1, (byte) 'x'); return buf.position(); } @Override int readExtraLongMessage(ByteBuffer buf) { buf.put((byte) 0xff); Wire.putUInt64(buf, 331); buf.put((byte) 0); // flag for (int i = 0; i < 5; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put("01".getBytes(ZMQ.CHARSET)); return buf.position(); } } jeromq-0.6.0/src/test/java/zmq/io/coder/V1EncoderTest.java000066400000000000000000000041351455771126300233220ustar00rootroot00000000000000package zmq.io.coder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Msg; import zmq.ZMQ; import zmq.io.coder.v1.V1Encoder; import zmq.util.Errno; import zmq.util.ValueReference; public class V1EncoderTest { private final EncoderBase encoder = new V1Encoder(new Errno(), 64); // as if it read data from socket private Msg readShortMessage() { return new Msg("hello".getBytes(ZMQ.CHARSET)); } // as if it read data from socket private Msg readLongMessage1() { Msg msg = new Msg(200); for (int i = 0; i < 20; i++) { msg.put("0123456789".getBytes(ZMQ.CHARSET)); } return msg; } @Test public void testReader() { Msg msg = readShortMessage(); encoder.loadMsg(msg); ValueReference ref = new ValueReference<>(); int outsize = encoder.encode(ref, 0); ByteBuffer out = ref.get(); assertThat(out, notNullValue()); assertThat(outsize, is(7)); assertThat(out.position(), is(7)); } @Test public void testReaderLong() { Msg msg = readLongMessage1(); ValueReference ref = new ValueReference<>(); int outsize = encoder.encode(ref, 0); assertThat(outsize, is(0)); ByteBuffer out = ref.get(); assertThat(out, nullValue()); encoder.loadMsg(msg); outsize = encoder.encode(ref, 64); assertThat(outsize, is(64)); out = ref.get(); int position = out.position(); int limit = out.limit(); assertThat(limit, is(64)); assertThat(position, is(64)); ref.set(null); outsize = encoder.encode(ref, 64); assertThat(outsize, is(138)); out = ref.get(); position = out.position(); limit = out.limit(); assertThat(position, is(62)); assertThat(limit, is(200)); } } jeromq-0.6.0/src/test/java/zmq/io/coder/V2DecoderTest.java000066400000000000000000000031641455771126300233120ustar00rootroot00000000000000package zmq.io.coder; import java.nio.ByteBuffer; import org.junit.Before; import zmq.ZMQ; import zmq.io.coder.v2.V2Decoder; import zmq.io.coder.v2.V2Protocol; import zmq.msg.MsgAllocatorThreshold; import zmq.util.Errno; import zmq.util.Wire; public class V2DecoderTest extends AbstractDecoderTest { @Before public void setUp() { decoder = new V2Decoder(new Errno(), 64, 512, new MsgAllocatorThreshold()); } // as if it read data from socket @Override int readShortMessage(ByteBuffer buf) { buf.put((byte) V2Protocol.MORE_FLAG); // flag buf.put((byte) 5); buf.put("hello".getBytes(ZMQ.CHARSET)); return buf.position(); } // as if it read data from socket @Override int readLongMessage1(ByteBuffer buf) { buf.put((byte) 1); // flag buf.put((byte) 200); for (int i = 0; i < 6; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put("01".getBytes(ZMQ.CHARSET)); return buf.position(); } @Override int readLongMessage2(ByteBuffer buf) { for (int i = 0; i < 13; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put(buf.position() - 1, (byte) 'x'); return buf.position(); } @Override int readExtraLongMessage(ByteBuffer buf) { buf.put((byte) V2Protocol.LARGE_FLAG); // flag Wire.putUInt64(buf, 330); for (int i = 0; i < 5; i++) { buf.put("0123456789".getBytes(ZMQ.CHARSET)); } buf.put("01".getBytes(ZMQ.CHARSET)); return buf.position(); } } jeromq-0.6.0/src/test/java/zmq/io/coder/V2EncoderTest.java000066400000000000000000000041361455771126300233240ustar00rootroot00000000000000package zmq.io.coder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Msg; import zmq.ZMQ; import zmq.io.coder.v2.V2Encoder; import zmq.util.Errno; import zmq.util.ValueReference; public class V2EncoderTest { private final EncoderBase encoder = new V2Encoder(new Errno(), 64); // as if it read data from socket private Msg readShortMessage() { return new Msg("hello".getBytes(ZMQ.CHARSET)); } // as if it read data from socket private Msg readLongMessage1() { Msg msg = new Msg(200); for (int i = 0; i < 20; i++) { msg.put("0123456789".getBytes(ZMQ.CHARSET)); } return msg; } @Test public void testReader() { Msg msg = readShortMessage(); encoder.loadMsg(msg); ValueReference ref = new ValueReference<>(); int outsize = encoder.encode(ref, 0); ByteBuffer out = ref.get(); assertThat(out, notNullValue()); assertThat(outsize, is(7)); assertThat(out.position(), is(7)); } @Test public void testReaderLong() { Msg msg = readLongMessage1(); ValueReference ref = new ValueReference<>(); int outsize = encoder.encode(ref, 0); assertThat(outsize, is(0)); ByteBuffer out = ref.get(); assertThat(out, nullValue()); encoder.loadMsg(msg); outsize = encoder.encode(ref, 64); assertThat(outsize, is(64)); out = ref.get(); int position = out.position(); int limit = out.limit(); assertThat(limit, is(64)); assertThat(position, is(64)); ref.set(null); outsize = encoder.encode(ref, 64); assertThat(outsize, is(138)); out = ref.get(); position = out.position(); limit = out.limit(); assertThat(position, is(62)); assertThat(limit, is(200)); } } jeromq-0.6.0/src/test/java/zmq/io/mechanism/000077500000000000000000000000001455771126300206765ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/io/mechanism/MechanismTester.java000066400000000000000000000123031455771126300246330ustar00rootroot00000000000000package zmq.io.mechanism; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import zmq.Ctx; import zmq.Helper; import zmq.Msg; import zmq.Options; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.TestUtils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; class MechanismTester { abstract static class TestContext { Ctx zctxt; SocketBase server; SocketBase client; SocketBase zapHandler; final String host = "tcp://127.0.0.1:*"; } static Boolean runTest(C testCtx, boolean withzap, Function tested, BiFunction, ZapHandler> zapProvider, Runnable configurator) throws InterruptedException { testCtx.zctxt = ZMQ.createContext(); // Future to hold the result of Zap handler processing // true: zap allowed the connexion // false: zap refused the connexion // null: zap didn't do any processing CompletableFuture zapFuture = new CompletableFuture<>(); Thread zapThread; if (withzap) { testCtx.zapHandler = ZMQ.socket(testCtx.zctxt, ZMQ.ZMQ_REP); ZapHandler handler = zapProvider.apply(testCtx.zapHandler, zapFuture); zapThread = handler.start(); if (zapFuture.isDone()) { // zap future should not be already finished try { assertThat(zapFuture.get().toString(), false); } catch (InterruptedException | ExecutionException e) { throw new IllegalStateException(e); } } } else { zapFuture.complete(null); zapThread = null; testCtx.zapHandler = null; } testCtx.server = ZMQ.socket(testCtx.zctxt, ZMQ.ZMQ_DEALER); assertThat(testCtx.server, notNullValue()); testCtx.client = ZMQ.socket(testCtx.zctxt, ZMQ.ZMQ_DEALER); assertThat(testCtx.client, notNullValue()); configurator.run(); boolean isSuccess = tested.apply(testCtx); // Exchange messages only if both sockets are used, some tests uses a plain socket if (testCtx.server != null && testCtx.client != null) { if (isSuccess) { Helper.bounce(testCtx.server, testCtx.client); } else { Helper.expectBounceFail(testCtx.server, testCtx.client); } } Optional.ofNullable(testCtx.client).ifPresent(ZMQ::closeZeroLinger); Optional.ofNullable(testCtx.server).ifPresent(ZMQ::closeZeroLinger); // Wait until ZAP handler terminates //Optional.ofNullable(testCtx.zapHandler).ifPresent(ZMQ::closeZeroLinger); Optional.ofNullable(zapThread).ifPresent(t -> { try { t.interrupt(); t.join(5000); } catch (InterruptedException e) { throw new IllegalStateException(e); } }); // Shutdown ZMQ.term(testCtx.zctxt); try { return zapFuture.get(5, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new IllegalStateException(e); } } public static boolean testRawSocket(T ctx) { try { boolean rc; int timeout = 250; ZMQ.setSocketOption(ctx.server, ZMQ.ZMQ_RCVTIMEO, timeout); rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); int port = TestUtils.port((String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT)); ZMQ.closeZeroLinger(ctx.client); ctx.client = null; try (Socket sock = new Socket("127.0.0.1", port)) { // send anonymous ZMTP/1.0 greeting OutputStream out = sock.getOutputStream(); out.write(("1" + 0x00).getBytes(ZMQ.CHARSET)); // send sneaky message that shouldn't be received out.write( ("8" + 0x00 + "sneaky" + 0x00) .getBytes(ZMQ.CHARSET)); Msg msg = ZMQ.recv(ctx.server, 0); assertThat(msg, nullValue()); } return false; } catch (IOException e) { throw new RuntimeException(e); } } public static void checkOptions(Mechanisms mechanism, Consumer setOptions) { Options opt = new Options(); setOptions.accept(opt); mechanism.check(opt); } private MechanismTester() { // static only class } } jeromq-0.6.0/src/test/java/zmq/io/mechanism/SecurityCurveTest.java000066400000000000000000000252641455771126300252260ustar00rootroot00000000000000package zmq.io.mechanism; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; import org.junit.Test; import zmq.SocketBase; import zmq.ZMQ; import zmq.io.mechanism.curve.Curve; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; public class SecurityCurveTest { private static class CurveTestContext extends MechanismTester.TestContext { String serverPublic; String serverSecret; String clientPublic; String clientSecret; } private Boolean runTest(boolean withzap, Function tested) throws InterruptedException { CurveTestContext testCtx = new CurveTestContext(); // Generate new keypairs for this test Curve cryptoBox = new Curve(); String[] clientKeys = cryptoBox.keypairZ85(); testCtx.clientPublic = clientKeys[0]; testCtx.clientSecret = clientKeys[1]; String[] serverKeys = cryptoBox.keypairZ85(); testCtx.serverPublic = serverKeys[0]; testCtx.serverSecret = serverKeys[1]; BiFunction, ZapHandler> zapProvider = (s, f) -> new ZapHandler(s, f, testCtx.clientPublic); Runnable configurator = () -> { // Preconfigure server with valid identity, might be changed for individual tests ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_CURVE_SERVER, true); ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_CURVE_SECRETKEY, testCtx.serverSecret); ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_IDENTITY, "IDENT"); // Preconfigure client with valid identity, might be changed for individual tests ZMQ.setSocketOption(testCtx.client, ZMQ.ZMQ_CURVE_SERVERKEY, testCtx.serverPublic); ZMQ.setSocketOption(testCtx.client, ZMQ.ZMQ_CURVE_PUBLICKEY, testCtx.clientPublic); ZMQ.setSocketOption(testCtx.client, ZMQ.ZMQ_CURVE_SECRETKEY, testCtx.clientSecret); }; return MechanismTester.runTest(testCtx, withzap, tested, zapProvider, configurator); } private boolean doSuccess(CurveTestContext ctx) { boolean rc; rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return true; } @Test public void testSuccessWithZap() throws InterruptedException { assertThat(runTest(true, this::doSuccess), is(true)); } @Test public void testSuccessNoZap() throws InterruptedException { assertThat(runTest(false, this::doSuccessInverted), nullValue()); } private boolean doSuccessInverted(CurveTestContext ctx) { boolean rc; rc = ZMQ.bind(ctx.client, ctx.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(ctx.client, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.server, host); assertThat(rc, is(true)); return true; } @Test public void testSuccessInvertedWithZap() throws InterruptedException { assertThat(runTest(true, this::doSuccessInverted), is(true)); } @Test public void testSuccessInvertedNoZap() throws InterruptedException { assertThat(runTest(false, this::doSuccessInverted), nullValue()); } @Test public void testGarbageClientSecretKeyZap() throws InterruptedException { Boolean zapCheck = runTest(true, ctx -> { boolean rc; rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_CURVE_SECRETKEY, "0000000000000000000000000000000000000000"); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, nullValue()); } @Test public void testGarbageServerSecretKeyZap() throws InterruptedException { Boolean zapCheck = runTest(true, ctx -> { boolean rc; ZMQ.setSocketOption(ctx.server, ZMQ.ZMQ_CURVE_SECRETKEY, "0000000000000000000000000000000000000000"); rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, nullValue()); } @Test public void testBogusClientKey() throws InterruptedException { // Check CURVE security with bogus client credentials // This must be caught by the ZAP handler Boolean zapCheck = runTest(true, ctx -> { boolean rc; rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); Curve cryptoBox = new Curve(); String[] bogus = cryptoBox.keypairZ85(); String bogusPublic = bogus[0]; String bogusSecret = bogus[1]; ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_CURVE_PUBLICKEY, bogusPublic); ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_CURVE_SECRETKEY, bogusSecret); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, is(false)); } @Test(timeout = 5000) public void testBogusClientInvertedKey() throws InterruptedException { // Check CURVE security with bogus client credentials // This must be caught by the ZAP handler Boolean zapCheck = runTest(true, ctx -> { boolean rc; rc = ZMQ.bind(ctx.client, ctx.host); assertThat(rc, is(true)); Curve cryptoBox = new Curve(); String[] bogus = cryptoBox.keypairZ85(); String bogusPublic = bogus[0]; String bogusSecret = bogus[1]; ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_CURVE_PUBLICKEY, bogusPublic); ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_CURVE_SECRETKEY, bogusSecret); String host = (String) ZMQ.getSocketOptionExt(ctx.client, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.server, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, is(false)); } @Test public void testNullClient() throws InterruptedException { // Check CURVE security with bogus client credentials // This must be caught by the ZAP handler Boolean zapCheck = runTest(false, ctx -> { boolean rc; rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); ZMQ.closeZeroLinger(ctx.client); ctx.client = ZMQ.socket(ctx.zctxt, ZMQ.ZMQ_DEALER); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, nullValue()); } @Test public void testPlainClient() throws InterruptedException { // Check CURVE security with bogus client credentials // This must be caught by the ZAP handler Boolean zapCheck = runTest(false, ctx -> { boolean rc; rc = ZMQ.bind(ctx.server, ctx.host); assertThat(rc, is(true)); ZMQ.closeZeroLinger(ctx.client); ctx.client = ZMQ.socket(ctx.zctxt, ZMQ.ZMQ_DEALER); ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_PLAIN_USERNAME, "user"); ZMQ.setSocketOption(ctx.client, ZMQ.ZMQ_PLAIN_PASSWORD, "pass"); String host = (String) ZMQ.getSocketOptionExt(ctx.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(ctx.client, host); assertThat(rc, is(true)); return false; }); assertThat(zapCheck, nullValue()); } @Test public void testRawSocket() throws InterruptedException { // Unauthenticated messages from a vanilla socket shouldn't be received Boolean zapCheck = runTest(false, MechanismTester::testRawSocket); assertThat(zapCheck, nullValue()); } @Test(expected = IllegalStateException.class) public void inconsistent1() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, null); }); } @Test(expected = IllegalStateException.class) public void inconsistent2() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, null); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[32]); }); } @Test(expected = IllegalStateException.class) public void inconsistent3() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[31]); }); } @Test(expected = IllegalStateException.class) public void inconsistent4() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[31]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[32]); }); } @Test(expected = IllegalStateException.class) public void inconsistent5() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SERVERKEY, new byte[31]); }); } @Test public void consistent1() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[32]); }); } @Test public void consistent2() { MechanismTester.checkOptions(Mechanisms.CURVE, opt -> { opt.setSocketOpt(ZMQ.ZMQ_CURVE_PUBLICKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SECRETKEY, new byte[32]); opt.setSocketOpt(ZMQ.ZMQ_CURVE_SERVERKEY, new byte[32]); }); } } jeromq-0.6.0/src/test/java/zmq/io/mechanism/SecurityNullTest.java000066400000000000000000000063361455771126300250530ustar00rootroot00000000000000package zmq.io.mechanism; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; import org.junit.Test; import zmq.SocketBase; import zmq.ZMQ; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; public class SecurityNullTest { private static class NullTestContext extends MechanismTester.TestContext { // Nothing to add } private Boolean runTest(boolean withzap, Function tested) throws InterruptedException { NullTestContext testCtx = new NullTestContext(); BiFunction, ZapHandler> zapProvider = ZapHandler::new; Runnable configurator = () -> ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_IDENTITY, "IDENT"); return MechanismTester.runTest(testCtx, withzap, tested, zapProvider, configurator); } @Test(timeout = 5000) public void testNoZap() throws InterruptedException { // We first test client/server with no ZAP domain Boolean status = runTest(false, tctxt -> { boolean rc; rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return true; }); assertThat(status, nullValue()); } @Test(timeout = 5000) public void testZap() throws InterruptedException { // Now use the right domain, the test must pass Boolean status = runTest(true, tctxt -> { boolean rc; ZMQ.setSocketOption(tctxt.server, ZMQ.ZMQ_ZAP_DOMAIN, "TEST"); rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return true; }); assertThat(status, is(true)); } @Test(timeout = 5000) public void testWrongDomain() throws InterruptedException { // Define a ZAP domain for the server; this enables // authentication. We're using the wrong domain so this test // must fail. Boolean status = runTest(true, tctxt -> { boolean rc; ZMQ.setSocketOption(tctxt.server, ZMQ.ZMQ_ZAP_DOMAIN, "WRONG"); rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return false; }); assertThat(status, is(false)); } @Test(timeout = 5000) public void testRawSocket() throws InterruptedException { // Unauthenticated messages from a vanilla socket shouldn't be received Boolean zapCheck = runTest(false, MechanismTester::testRawSocket); assertThat(zapCheck, nullValue()); } } jeromq-0.6.0/src/test/java/zmq/io/mechanism/SecurityPlainTest.java000066400000000000000000000155201455771126300251770ustar00rootroot00000000000000package zmq.io.mechanism; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Function; import org.junit.Test; import zmq.SocketBase; import zmq.ZMQ; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; public class SecurityPlainTest { private static class PlainTestContext extends MechanismTester.TestContext { String user; String password; } private Boolean runTest(boolean withzap, Function tested) throws InterruptedException { PlainTestContext testCtx = new PlainTestContext(); testCtx.user = "admin"; testCtx.password = "password"; BiFunction, ZapHandler> zapProvider = (s, f) -> new ZapHandler(s, f, "admin", "password"); Runnable configurator = () -> { ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_IDENTITY, "IDENT"); ZMQ.setSocketOption(testCtx.server, ZMQ.ZMQ_PLAIN_SERVER, true); }; return MechanismTester.runTest(testCtx, withzap, tested, zapProvider, configurator); } public boolean runValid(PlainTestContext tctxt) { boolean rc; rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_USERNAME, tctxt.user); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_PASSWORD, tctxt.password); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return true; } @Test(timeout = 5000) public void testNoZap() throws InterruptedException { // We first test client/server with no ZAP domain Boolean status = runTest(false, this::runValid); assertThat(status, nullValue()); } @Test(timeout = 5000) public void testZap() throws InterruptedException { // We first test client/server with no ZAP domain Boolean status = runTest(true, this::runValid); assertThat(status, is(true)); } @Test(timeout = 5000) public void testZapInverted() throws InterruptedException { // When ZAP is not used, always accept Boolean status = runTest(false, tctxt -> { boolean rc; ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_USERNAME, tctxt.user); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_PASSWORD, tctxt.password); rc = ZMQ.bind(tctxt.client, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.client, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(tctxt.server, host); assertThat(rc, is(true)); return true; }); assertThat(status, nullValue()); } @Test(timeout = 5000) public void testBothServer() throws InterruptedException { // We first test client/server with no ZAP domain Boolean status = runTest(true, tctxt -> { boolean rc; rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_SERVER, true); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return false; }); assertThat(status, nullValue()); } @Test(timeout = 5000) public void testFailedLoginZap() throws InterruptedException { // We first test client/server with no ZAP domain Boolean status = runTest(true, tctxt -> { boolean rc; rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_USERNAME, "wronguser"); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_PASSWORD, "wrongpass"); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return false; }); assertThat(status, is(false)); } @Test(timeout = 5000) public void testSuccessBadPasswordNoZap() throws InterruptedException { // When ZAP is not used, always accept Boolean status = runTest(false, tctxt -> { boolean rc; rc = ZMQ.bind(tctxt.server, tctxt.host); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(tctxt.server, ZMQ.ZMQ_LAST_ENDPOINT); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_USERNAME, "wronguser"); ZMQ.setSocketOption(tctxt.client, ZMQ.ZMQ_PLAIN_PASSWORD, "wrongpass"); rc = ZMQ.connect(tctxt.client, host); assertThat(rc, is(true)); return true; }); assertThat(status, nullValue()); } @Test public void testRawSocket() throws InterruptedException { // Unauthenticated messages from a vanilla socket shouldn't be received Boolean zapCheck = runTest(false, MechanismTester::testRawSocket); assertThat(zapCheck, nullValue()); } @Test(expected = IllegalStateException.class) public void inconsistent1() { MechanismTester.checkOptions(Mechanisms.PLAIN, opt -> { opt.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, null); opt.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, "plainPassword"); }); } @Test(expected = IllegalStateException.class) public void inconsistent2() { MechanismTester.checkOptions(Mechanisms.PLAIN, opt -> { opt.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, "plainUsername"); opt.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, null); }); } @Test(expected = IllegalStateException.class) public void inconsistent3() { MechanismTester.checkOptions(Mechanisms.PLAIN, opt -> { opt.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, String.format("%256d", 1)); opt.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, "plainPassword"); }); } @Test(expected = IllegalStateException.class) public void inconsistent4() { MechanismTester.checkOptions(Mechanisms.PLAIN, opt -> { opt.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, "plainUsername"); opt.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, String.format("%256d", 1)); }); } @Test public void consistent() { MechanismTester.checkOptions(Mechanisms.PLAIN, opt -> { opt.setSocketOpt(ZMQ.ZMQ_PLAIN_USERNAME, "plainUsername"); opt.setSocketOpt(ZMQ.ZMQ_PLAIN_PASSWORD, "plainPassword"); }); } } jeromq-0.6.0/src/test/java/zmq/io/mechanism/ZapHandler.java000066400000000000000000000170631455771126300236000ustar00rootroot00000000000000package zmq.io.mechanism; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Z85; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; class ZapHandler implements Runnable { private final SocketBase handler; private final String mechanism; private final String clientPublic; private final String username; private final String password; private final CompletableFuture zapFuture; public ZapHandler(SocketBase handler, CompletableFuture zapFuture, String clientPublic) { this.handler = handler; this.clientPublic = clientPublic; this.zapFuture = zapFuture; this.username = null; this.password = null; this.mechanism = "CURVE"; } public ZapHandler(SocketBase handler, CompletableFuture zapFuture, String username, String password) { this.handler = handler; this.clientPublic = null; this.zapFuture = zapFuture; this.username = username; this.password = password; this.mechanism = "PLAIN"; } public ZapHandler(SocketBase handler, CompletableFuture zapFuture) { this.handler = handler; this.clientPublic = null; this.zapFuture = zapFuture; this.username = null; this.password = null; this.mechanism = "NULL"; } public Thread start() throws InterruptedException { CountDownLatch zapStarted = new CountDownLatch(1); // Spawn ZAP handler Thread zapThread = new Thread(() -> { try { assertThat(handler, notNullValue()); boolean rc = ZMQ.bind(handler, "inproc://zeromq.zap.01"); assertThat(rc, is(true)); } catch (Exception | AssertionError ex) { ex.printStackTrace(); zapFuture.completeExceptionally(ex); } finally { zapStarted.countDown(); } run(); }); zapThread.setUncaughtExceptionHandler((t, e) -> { e.printStackTrace(); zapFuture.completeExceptionally(e); }); zapThread.setName("ZAPHANDLER"); zapThread.start(); assertThat(zapStarted.await(5, TimeUnit.SECONDS), is(true)); return zapThread; } @Override public void run() { // Process ZAP requests forever try { while (true) { Msg version = ZMQ.recv(handler, 0); if (version != null) { assertThat("No more message", version.hasMore()); assertThat(new String(version.data(), ZMQ.CHARSET), is("1.0")); Msg sequence = ZMQ.recv(handler, 0); assertThat("No more message", sequence.hasMore()); Msg domain = ZMQ.recv(handler, 0); assertThat("No more message", domain.hasMore()); String clientDomain = new String(domain.data(), ZMQ.CHARSET); Msg address = ZMQ.recv(handler, 0); assertThat("No more message", address.hasMore()); Msg identity = ZMQ.recv(handler, 0); assertThat("No more message", identity.hasMore()); assertThat(new String(identity.data(), ZMQ.CHARSET), is("IDENT")); Msg mechanismMsg = ZMQ.recv(handler, 0); assertThat("No more message", "NULL".equals(mechanism) || mechanismMsg.hasMore()); String clientMechanism = new String(mechanismMsg.data(), ZMQ.CHARSET); assertThat(clientMechanism, is(mechanism)); String clientKeyText = null; String clientUsername = null; String clientPassword = null; if ("CURVE".equals(mechanism)) { Msg clientKey = ZMQ.recv(handler, 0); assertThat("More message", ! clientKey.hasMore()); clientKeyText = Z85.encode(clientKey.data(), clientKey.size()); } else if ("PLAIN".equals(mechanism)) { Msg username = ZMQ.recv(handler, 0); assertThat("No more message", username.hasMore()); Msg password = ZMQ.recv(handler, 0); assertThat("No more message", ! password.hasMore()); clientUsername = new String(username.data(), ZMQ.CHARSET); clientPassword = new String(password.data(), ZMQ.CHARSET); } int ret = ZMQ.send(handler, version, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(3)); ret = ZMQ.send(handler, sequence, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); boolean authentified; String errorMessage; String userId; if ("CURVE".equals(mechanism)) { authentified = clientKeyText.equals(clientPublic); errorMessage = "Invalid client public key"; userId = clientPublic; } else if ("PLAIN".equals(mechanism)) { authentified = username.equals(clientUsername) && password.equals(clientPassword); errorMessage = "Invalid username or password"; userId = clientUsername; } else if ("NULL".equals(mechanism)) { authentified = "TEST".equals(clientDomain); errorMessage = "BAD DOMAIN"; userId = ""; } else { authentified = false; errorMessage = "Unknown mechanism"; userId = ""; } ret = ZMQ.send(handler, authentified ? "200" : "400", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(3)); if (authentified) { ret = ZMQ.send(handler, "OK", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(2)); ret = ZMQ.send(handler, userId, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(userId.length())); } else { ret = ZMQ.send(handler, errorMessage, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(errorMessage.length())); ret = ZMQ.send(handler, "", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(0)); } ret = ZMQ.send(handler, "", 0); assertThat(ret, is(0)); if (! zapFuture.isDone()) { zapFuture.complete(authentified); } } else { if (! zapFuture.isDone()) { zapFuture.complete(null); } break; } } } catch (Exception | AssertionError ex) { ex.printStackTrace(); zapFuture.completeExceptionally(ex); } finally { ZMQ.closeZeroLinger(handler); } } } jeromq-0.6.0/src/test/java/zmq/io/net/000077500000000000000000000000001455771126300175205ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/io/net/BindSrcAddressTest.java000066400000000000000000000014441455771126300240600ustar00rootroot00000000000000package zmq.io.net; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class BindSrcAddressTest { @Test public void test() throws IOException { Ctx ctx = ZMQ.createContext(); assert (ctx != null); SocketBase socket = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assert (socket != null); int port1 = Utils.findOpenPort(); int port2 = Utils.findOpenPort(); int port3 = Utils.findOpenPort(); boolean rc = ZMQ.connect(socket, "tcp://127.0.0.1:0;localhost:" + port1); assert (rc); rc = ZMQ.connect(socket, "tcp://localhost:" + port3 + ";localhost:" + port2); assert (rc); ZMQ.close(socket); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/io/net/TestAddress.java000066400000000000000000000016011455771126300226060ustar00rootroot00000000000000package zmq.io.net; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.zeromq.ZMQException; public class TestAddress { @Test public void testToNotResolvedToString() { Address addr = new Address("tcp", "google.com:90"); String saddr = addr.toString(); assertThat(saddr, is("tcp://google.com:90")); } @Test public void testResolvedToString() { Address addr = new Address("tcp", "google.com:90"); addr.resolve(false); String resolved = addr.toString(); assertTrue(resolved.matches("tcp://\\d+\\.\\d+\\.\\d+\\.\\d+:90")); } @Test(expected = ZMQException.class) public void testInvalid() { new Address("tcp", "ggglocalhostxxx.google.com:80").resolve(false); } } jeromq-0.6.0/src/test/java/zmq/io/net/tcp/000077500000000000000000000000001455771126300203065ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/io/net/tcp/KeepAliveTest.java000066400000000000000000000047471455771126300236720ustar00rootroot00000000000000package zmq.io.net.tcp; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketOption; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import org.junit.Assert; import org.junit.Test; import zmq.Options; public class KeepAliveTest { @Test public void testConsistent() throws IOException { try { int javaspec = Integer.parseInt(System.getProperty("java.specification.version")); // Test fails for jvm lest that 13 if (javaspec >= 13) { Class eso = Options.class.getClassLoader().loadClass("jdk.net.ExtendedSocketOptions"); SocketOption count = (SocketOption) eso.getField("TCP_KEEPCOUNT").get(null); SocketOption idle = (SocketOption) eso.getField("TCP_KEEPIDLE").get(null); SocketOption interval = (SocketOption) eso.getField("TCP_KEEPINTERVAL").get(null); Method socketgetOption = Socket.class.getMethod("getOption", SocketOption.class); Method serversocketgetOption = ServerSocket.class.getMethod("getOption", SocketOption.class); Assert.assertTrue(TcpUtils.WITH_EXTENDED_KEEPALIVE); SocketChannel sc = SocketChannel.open(); TcpUtils.tuneTcpKeepalives(sc, 1, 2, 3, 4); Assert.assertEquals(2, socketgetOption.invoke(sc.socket(), count)); Assert.assertEquals(3, socketgetOption.invoke(sc.socket(), idle)); Assert.assertEquals(4, socketgetOption.invoke(sc.socket(), interval)); ServerSocketChannel ssc = ServerSocketChannel.open(); TcpUtils.tuneTcpKeepalives(ssc, 1, 2, 3, 4); Assert.assertEquals(2, serversocketgetOption.invoke(ssc.socket(), count)); Assert.assertEquals(3, serversocketgetOption.invoke(ssc.socket(), idle)); Assert.assertEquals(4, serversocketgetOption.invoke(ssc.socket(), interval)); } } catch (NumberFormatException ex) { // java 1.8, not a problem as keepalive is not handled } catch (NoSuchMethodException | ClassNotFoundException | NoSuchFieldException | IllegalAccessException | InvocationTargetException e) { // Not defined, just skip the test } } } jeromq-0.6.0/src/test/java/zmq/io/net/tcp/TcpAddressTest.java000066400000000000000000000133121455771126300240450ustar00rootroot00000000000000package zmq.io.net.tcp; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetSocketAddress; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.zeromq.ZMQException; import zmq.ZError; import zmq.io.net.Address; import zmq.io.net.NetProtocol; import zmq.util.Utils; public class TcpAddressTest { @Test public void parsesIpv6AddressSimple() throws IOException { String addressString = "2000::a1"; int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, addressString + ":" + port); addr.resolve(true); InetSocketAddress expected = new InetSocketAddress(addressString, port); Address.IZAddress resolved = addr.resolved(); assertEquals(expected, resolved.address()); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); Assert.assertEquals(port, sa.getPort()); } @Test public void parsesIpv6AddressBracket() throws IOException { String addressString = "2000::a1"; int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, "[" + addressString + "]:" + port); addr.resolve(true); InetSocketAddress expected = new InetSocketAddress(addressString, port); Address.IZAddress resolved = addr.resolved(); assertEquals(expected, resolved.address()); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); Assert.assertEquals(port, sa.getPort()); } @Test public void parsesIpv6AddressNotWanted() throws IOException { try { String addressString = "2000::a1"; int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, addressString + ":" + port); addr.resolve(false); InetSocketAddress expected = new InetSocketAddress(addressString, port); Address.IZAddress resolved = addr.resolved(); assertEquals(expected, resolved.address()); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); Assert.assertEquals(port, sa.getPort()); Assert.fail(); } catch (ZMQException e) { Assert.assertEquals(ZError.EADDRNOTAVAIL, e.getErrorCode()); Assert.assertEquals("2000::a1 not found matching IPv4/IPv6 settings", e.getMessage()); } } @Ignore // Fails on both Circleci and Travis @Test public void testGoodIPv6Google() { Address addr = new Address(NetProtocol.tcp, "www.google.com:80"); addr.resolve(true); Address.IZAddress resolved = addr.resolved(); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); Assert.assertEquals(80, sa.getPort()); } @Test public void testGoodIP46Google() { Address addr = new Address(NetProtocol.tcp, "www.google.com:80"); addr.resolve(false); Address.IZAddress resolved = addr.resolved(); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet4Address); Assert.assertEquals(80, sa.getPort()); } @Test public void testBad() { try { Address addr = new Address(NetProtocol.tcp, "ggglocalhostxxx.google.com:80"); addr.resolve(true); addr.resolved(); Assert.fail(); } catch (ZMQException e) { Assert.assertEquals(ZError.EADDRNOTAVAIL, e.getErrorCode()); Assert.assertEquals(e.getCause().getMessage(), e.getMessage()); } } @Test public void testUnspecifiedIPv6DoubleColon() throws IOException { int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, ":::" + port); addr.resolve(true); Address.IZAddress resolved = addr.resolved(); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); String hostString = sa.getHostString(); Assert.assertTrue("::".equals(hostString) || "0:0:0:0:0:0:0:0".equals(hostString)); Assert.assertEquals(port, sa.getPort()); } @Test public void testUnspecifiedIPv6Star() throws IOException { int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, "*:" + port); addr.resolve(true); Address.IZAddress resolved = addr.resolved(); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet6Address); String hostString = sa.getHostString(); Assert.assertTrue("::".equals(hostString) || "0:0:0:0:0:0:0:0".equals(hostString)); Assert.assertEquals(port, sa.getPort()); } @Test public void testUnspecifiedIPv4() throws IOException { int port = Utils.findOpenPort(); Address addr = new Address(NetProtocol.tcp, "*:" + port); addr.resolve(false); Address.IZAddress resolved = addr.resolved(); InetSocketAddress sa = (InetSocketAddress) resolved.address(); Assert.assertTrue(sa.getAddress() instanceof Inet4Address); Assert.assertEquals("0.0.0.0", sa.getHostString()); Assert.assertEquals(port, sa.getPort()); } } jeromq-0.6.0/src/test/java/zmq/pipe/000077500000000000000000000000001455771126300172605ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/pipe/ConflateTest.java000066400000000000000000000022331455771126300225160ustar00rootroot00000000000000package zmq.pipe; import org.junit.Assert; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class ConflateTest { @Test public void test() throws InterruptedException { Ctx ctx = ZMQ.init(1); assert (ctx != null); SocketBase in = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assert (in != null); String host = "tcp://localhost:*"; int conflate = 1; ZMQ.setSocketOption(in, ZMQ.ZMQ_CONFLATE, conflate); boolean rc = ZMQ.bind(in, host); assert (rc); SocketBase out = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assert (out != null); String ep = (String) ZMQ.getSocketOptionExt(in, ZMQ.ZMQ_LAST_ENDPOINT); rc = ZMQ.connect(out, ep); assert (rc); int messageCount = 20; for (int j = 0; j < messageCount; ++j) { int count = Helper.send(out, Integer.toString(j)); assert (count > 0); } Thread.sleep(200); String recvd = Helper.recv(in); Assert.assertEquals("19", recvd); ZMQ.close(in); ZMQ.close(out); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/pipe/YQueueTest.java000066400000000000000000000020301455771126300221730ustar00rootroot00000000000000package zmq.pipe; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Msg; import zmq.ZMQ; public class YQueueTest { @Test public void testReuse() { // yqueue has a first empty entry YQueue p = new YQueue<>(3); Msg m1 = new Msg(1); Msg m2 = new Msg(2); Msg m3 = new Msg(3); Msg m4 = new Msg(4); Msg m5 = new Msg(5); Msg m6 = new Msg(6); Msg m7 = new Msg(7); m7.put("1234567".getBytes(ZMQ.CHARSET)); p.push(m1); assertThat(p.backPos(), is(1)); p.push(m2); // might allocated new chunk p.push(m3); assertThat(p.backPos(), is(3)); assertThat(p.frontPos(), is(0)); p.pop(); p.pop(); p.pop(); // offer the old chunk assertThat(p.frontPos(), is(3)); p.push(m4); p.push(m5); // might reuse the old chunk p.push(m6); assertThat(p.backPos(), is(0)); } } jeromq-0.6.0/src/test/java/zmq/poll/000077500000000000000000000000001455771126300172715ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/poll/PollEvents.java000066400000000000000000000005321455771126300222270ustar00rootroot00000000000000package zmq.poll; class PollEvents implements IPollEvents { @Override public void timerEvent(int id) { } @Override public void outEvent() { } @Override public void inEvent() { } @Override public void connectEvent() { } @Override public void acceptEvent() { } } jeromq-0.6.0/src/test/java/zmq/poll/PollerBaseTest.java000066400000000000000000000076451455771126300230400ustar00rootroot00000000000000package zmq.poll; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; public class PollerBaseTest { private final IPollEvents sink = new PollEvents(); @Test public void testNoTimer() { PollerBase poller = new PollerBaseTested(); long timeout = poller.executeTimers(); assertThat(timeout, is(0L)); poller.addTimer(1000, sink, 1); } @Test public void testOneTimer() { final PollerBaseTested poller = new PollerBaseTested(); poller.addTimer(1000, sink, 1); long timeout = poller.executeTimers(); assertThat(timeout, is(1000L)); assertThat(poller.isEmpty(), is(false)); poller.clock(200); timeout = poller.executeTimers(); assertThat(timeout, is(800L)); assertThat(poller.isEmpty(), is(false)); poller.clock(1000); timeout = poller.executeTimers(); assertThat(poller.isEmpty(), is(true)); assertThat(timeout, is(0L)); } @Test public void testCancelTimer() { final PollerBaseTested poller = new PollerBaseTested(); poller.addTimer(1000, sink, 1); long timeout = poller.executeTimers(); assertThat(timeout, is(1000L)); assertThat(poller.isEmpty(), is(false)); poller.cancelTimer(sink, 1); timeout = poller.executeTimers(); assertThat(timeout, is(0L)); assertThat(poller.isEmpty(), is(true)); } @Test public void testCancelTimerInTimerEvent() { final PollerBaseTested poller = new PollerBaseTested(); PollEvents sink = new PollEvents() { @Override public void timerEvent(int id) { // cancelTimer() is never called in timerEvent() poller.cancelTimer(this, id); } }; poller.addTimer(1000, sink, 1); poller.clock(1000); long rc = poller.executeTimers(); assertThat(rc, is(0L)); assertThat(poller.isEmpty(), is(true)); } @Test public void testAddTimerInTimerEvent() { final int id = 1; final PollerBaseTested poller = new PollerBaseTested(); final AtomicInteger counter = new AtomicInteger(); PollEvents sink = new PollEvents() { @Override public void timerEvent(int id) { counter.incrementAndGet(); poller.addTimer(100, this, id); } }; poller.addTimer(1000, sink, id); assertThat(poller.isEmpty(), is(false)); poller.clock(1000); long timeout = poller.executeTimers(); assertThat(counter.get(), is(1)); assertThat(poller.isEmpty(), is(false)); assertThat(timeout, is(100L)); poller.cancelTimer(sink, id); poller.clock(3000); timeout = poller.executeTimers(); assertThat(counter.get(), is(1)); assertThat(timeout, is(0L)); assertThat(poller.isEmpty(), is(true)); } @Test public void testAddTimer() { final PollerBaseTested poller = new PollerBaseTested(); poller.addTimer(1000, new PollEvents() { private boolean first = true; @Override public void timerEvent(int id) { if (first) { // expires at 2000 + 1000 poller.addTimer(2000, this, id); } first = false; } }, 1); poller.clock(1000); long timeout = poller.executeTimers(); assertThat(poller.isEmpty(), is(false)); assertThat(timeout, is(2000L)); poller.clock(3000); timeout = poller.executeTimers(); assertThat(timeout, is(0L)); assertThat(poller.isEmpty(), is(true)); } } jeromq-0.6.0/src/test/java/zmq/poll/PollerBaseTested.java000066400000000000000000000005611455771126300233370ustar00rootroot00000000000000package zmq.poll; class PollerBaseTested extends PollerBase { private long clock = 0; PollerBaseTested() { super("test", (s, r) -> Thread.currentThread()); } void clock(long clock) { this.clock = clock; } @Override long clock() { return clock; } @Override public void run() { } } jeromq-0.6.0/src/test/java/zmq/poll/PollerTest.java000066400000000000000000000050121455771126300222270ustar00rootroot00000000000000package zmq.poll; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedSelectorException; import java.nio.channels.Pipe; import java.nio.channels.Selector; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import zmq.Ctx; public class PollerTest { @Test(timeout = 1000) public void catchNotification() throws IOException, InterruptedException { CountDownLatch catched = new CountDownLatch(1); AtomicReference selectorRef = new AtomicReference<>(); Ctx ctx = new Ctx() { @Override public Selector createSelector() { selectorRef.set(super.createSelector()); return selectorRef.get(); } }; ctx.setNotificationExceptionHandler((t, e) -> { if (e instanceof ClosedSelectorException) { catched.countDown(); } }); Poller poller = new Poller(ctx, "test"); poller.start(); selectorRef.get().close(); catched.await(); } @Test(timeout = 1000) public void catchFailure() throws IOException, InterruptedException { CountDownLatch catched = new CountDownLatch(1); Ctx ctx = new Ctx(); ctx.setUncaughtExceptionHandler((t, e) -> { if (e instanceof OutOfMemoryError) { catched.countDown(); } }); Poller poller = new Poller(ctx, "test"); Pipe signaler = Pipe.open(); try (Pipe.SourceChannel source = signaler.source(); Pipe.SinkChannel sink = signaler.sink() ) { source.configureBlocking(false); Poller.Handle handle = poller.addHandle(signaler.source(), new IPollEvents() { @Override public void inEvent() { throw new OutOfMemoryError(); } }); poller.setPollIn(handle); poller.start(); sink.write(ByteBuffer.allocate(1)); catched.await(); } } @Test(timeout = 1000) public void threadFactory() throws InterruptedException { CountDownLatch catched = new CountDownLatch(1); Ctx ctx = new Ctx(); ctx.setThreadFactory((r, s) -> { catched.countDown(); return new Thread(r, s); }); new Poller(ctx, "test"); catched.await(); } } jeromq-0.6.0/src/test/java/zmq/proxy/000077500000000000000000000000001455771126300175045ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/proxy/ProxySingleSocketTest.java000066400000000000000000000062021455771126300246430ustar00rootroot00000000000000package zmq.proxy; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class ProxySingleSocketTest { private static class ServerTask implements Runnable { private final Ctx ctx; private final String host; public ServerTask(Ctx ctx, String host) { this.ctx = ctx; this.host = host; } @Override public void run() { SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); boolean rc = ZMQ.bind(rep, host); assertThat(rc, is(true)); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); ZMQ.setSocketOption(control, ZMQ.ZMQ_SUBSCRIBE, ""); rc = ZMQ.connect(control, "inproc://control"); assertThat(rc, is(true)); // Use rep as both frontend and backend ZMQ.proxy(rep, rep, null, control); ZMQ.close(rep); ZMQ.close(control); } } @Test public void testProxySingleSocket() throws IOException, InterruptedException { int port = Utils.findOpenPort(); String host = "tcp://127.0.0.1:" + port; // The main thread simply starts several clients and a server, and then // waits for the server to finish. Ctx ctx = ZMQ.createContext(); SocketBase req = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); boolean rc = ZMQ.connect(req, host); assertThat(rc, is(true)); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); rc = ZMQ.bind(control, "inproc://control"); assertThat(rc, is(true)); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(new ServerTask(ctx, host)); int ret = ZMQ.send(req, "msg1", 0); assertThat(ret, is(4)); System.out.print("."); Msg msg = ZMQ.recv(req, 0); System.out.print("."); assertThat(msg, notNullValue()); assertThat(new String(msg.data(), ZMQ.CHARSET), is("msg1")); ret = ZMQ.send(req, "msg22", 0); assertThat(ret, is(5)); System.out.print("."); msg = ZMQ.recv(req, 0); System.out.print("."); assertThat(msg, notNullValue()); assertThat(new String(msg.data(), ZMQ.CHARSET), is("msg22")); ret = ZMQ.send(control, ZMQ.PROXY_TERMINATE, 0); assertThat(ret, is(9)); System.out.println("."); ZMQ.close(control); ZMQ.close(req); executor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/proxy/ProxyTcpTest.java000066400000000000000000000165111455771126300230030ustar00rootroot00000000000000package zmq.proxy; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; import org.junit.Ignore; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.io.coder.Decoder; import zmq.io.coder.EncoderBase; import zmq.msg.MsgAllocatorThreshold; import zmq.util.Errno; import zmq.util.Utils; @Ignore public class ProxyTcpTest { static class Client extends Thread { private final int port; public Client(int port) { this.port = port; } @Override public void run() { System.out.println("Start client thread"); try { Socket s = new Socket("127.0.0.1", port); Helper.send(s, "helo"); Helper.send(s, "1234567890abcdefghizklmnopqrstuvwxyz"); Helper.send(s, "end"); Helper.send(s, "end"); s.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Stop client thread"); } } static class Dealer extends Thread { private final SocketBase s; private final String name; private final int port; public Dealer(Ctx ctx, String name, int port) { this.s = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); this.name = name; this.port = port; } @Override public void run() { System.out.println("Start dealer " + name); ZMQ.connect(s, "tcp://127.0.0.1:" + port); int i = 0; while (true) { Msg msg = s.recv(0); if (msg == null) { throw new RuntimeException("hello"); } System.out.println("DEALER " + name + " received " + msg); String data = new String(msg.data(), 0, msg.size(), ZMQ.CHARSET); Msg response; if ((i % 3) == 2) { response = new Msg(msg.size() + 3); response.put("OK ".getBytes(ZMQ.CHARSET)).put(msg.data()); } else { response = new Msg(msg.data()); } s.send(response, (i % 3) == 2 ? 0 : ZMQ.ZMQ_SNDMORE); i++; if (data.equals("end")) { break; } } s.close(); System.out.println("Stop dealer " + name); } } public static class ProxyDecoder extends Decoder { private final Step readHeader = this::readHeader; private final Step readBody = this::readBody; final byte[] header = new byte[4]; Msg msg; int size = -1; boolean identitySent = false; final Msg bottom; public ProxyDecoder(int bufsize, long maxmsgsize) { super(new Errno(), bufsize, maxmsgsize, new MsgAllocatorThreshold()); nextStep(header, 4, readHeader); bottom = new Msg(); bottom.setFlags(Msg.MORE); } private Step.Result readHeader() { size = Integer.parseInt(new String(header, ZMQ.CHARSET)); System.out.println("Received " + size); msg = new Msg(size); nextStep(msg, readBody); return Step.Result.MORE_DATA; } private Step.Result readBody() { System.out.println("Received body " + new String(msg.data(), ZMQ.CHARSET)); if (!identitySent) { Msg identity = new Msg(); // push(identity); identitySent = true; } nextStep(header, 4, readHeader); return Step.Result.DECODED; } } public static class ProxyEncoder extends EncoderBase { private final Runnable writeHeader = this::writeHeader; private final Runnable writeBody = this::writeBody; final ByteBuffer header = ByteBuffer.allocate(4); Msg msg; int size = -1; boolean messageReady; boolean identityReceived; public ProxyEncoder(int bufsize, long unused) { super(new Errno(), bufsize); nextStep(null, writeHeader, true); messageReady = false; identityReceived = false; } private void writeBody() { System.out.println("write body " + msg); nextStep(msg, writeHeader, !msg.hasMore()); } private void writeHeader() { msg = inProgress; if (msg == null) { return; } if (!identityReceived) { identityReceived = true; nextStep(header, msg.size() < 255 ? 2 : 10, writeBody, true); return; } else if (!messageReady) { messageReady = true; msg = inProgress; if (msg == null) { return; } } messageReady = false; System.out.println("write header " + msg.size()); header.clear(); header.put(String.format("%04d", msg.size()).getBytes(ZMQ.CHARSET)); header.flip(); nextStep(header, header.limit(), writeBody, false); } } static class Proxy extends Thread { private final Ctx ctx; private final int routerPort; private final int dealerPort; Proxy(Ctx ctx, int routerPort, int dealerPort) { this.ctx = ctx; this.routerPort = routerPort; this.dealerPort = dealerPort; } @Override public void run() { boolean rc; SocketBase routerBind = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(routerBind, notNullValue()); routerBind.setSocketOpt(ZMQ.ZMQ_DECODER, ProxyDecoder.class); routerBind.setSocketOpt(ZMQ.ZMQ_ENCODER, ProxyEncoder.class); rc = ZMQ.bind(routerBind, "tcp://127.0.0.1:" + routerPort); assertThat(rc, is(true)); SocketBase dealerBind = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealerBind, notNullValue()); rc = ZMQ.bind(dealerBind, "tcp://127.0.0.1:" + dealerPort); assertThat(rc, is(true)); ZMQ.proxy(routerBind, dealerBind, null); ZMQ.close(routerBind); ZMQ.close(dealerBind); } } @Test public void testProxyTcp() throws Exception { int routerPort = Utils.findOpenPort(); int dealerPort = Utils.findOpenPort(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); Proxy mt = new Proxy(ctx, routerPort, dealerPort); mt.start(); new Dealer(ctx, "A", dealerPort).start(); // new Dealer(ctx, "B", dealerPort).start(); ZMQ.sleep(1); Thread client = new Client(routerPort); client.start(); client.join(); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/proxy/ProxyTerminateTest.java000066400000000000000000000105321455771126300242020ustar00rootroot00000000000000package zmq.proxy; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class ProxyTerminateTest { private static class ServerTask implements Runnable { private final Ctx ctx; private final CompletableFuture resultHander; private final String hostFrontend; private final String hostBackend; public ServerTask(Ctx ctx, String hostFrontend, String hostBackend, CompletableFuture resultHander) { this.ctx = ctx; this.hostFrontend = hostFrontend; this.hostBackend = hostBackend; this.resultHander = resultHander; } @Override public void run() { SocketBase frontend = null; // Nice socket which is never read SocketBase backend = null; // Control socket receives terminate command from main over inproc SocketBase control = null; try { frontend = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(frontend, notNullValue()); ZMQ.setSocketOption(frontend, ZMQ.ZMQ_SUBSCRIBE, ""); boolean rc = ZMQ.bind(frontend, hostFrontend); assertThat(rc, is(true)); backend = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(backend, notNullValue()); rc = ZMQ.bind(frontend, hostBackend); assertThat(rc, is(true)); control = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); ZMQ.setSocketOption(control, ZMQ.ZMQ_SUBSCRIBE, ""); rc = ZMQ.connect(control, "inproc://control"); assertThat(rc, is(true)); // Connect backend to frontend via a proxy ZMQ.proxy(frontend, backend, null, control); resultHander.complete(true); } finally { if (frontend != null) { ZMQ.close(frontend); } if (backend != null) { ZMQ.close(backend); } if (control != null) { ZMQ.close(control); } } } } @Test(timeout = 5000) public void testProxyTerminate() throws IOException, InterruptedException, ExecutionException { int port = Utils.findOpenPort(); String frontend = "tcp://127.0.0.1:" + port; port = Utils.findOpenPort(); String backend = "tcp://127.0.0.1:" + port; // The main thread simply starts a basic steerable proxy server, publishes some messages, and then // waits for the server to terminate. Ctx ctx = ZMQ.createContext(); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); boolean rc = ZMQ.bind(control, "inproc://control"); assertThat(rc, is(true)); CompletableFuture resultHander = new CompletableFuture<>(); Thread thread = new Thread(new ServerTask(ctx, frontend, backend, resultHander)); thread.setUncaughtExceptionHandler((t, e) -> resultHander.completeExceptionally(e)); thread.start(); Thread.sleep(500); // Start a secondary publisher which writes data to the SUB-PUSH server socket SocketBase publisher = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(publisher, notNullValue()); rc = ZMQ.connect(publisher, frontend); assertThat(rc, is(true)); Thread.sleep(50); int ret = ZMQ.send(publisher, "This is a test", 0); assertThat(ret, is(14)); Thread.sleep(50); ret = ZMQ.send(publisher, "This is a test", 0); assertThat(ret, is(14)); Thread.sleep(50); ret = ZMQ.send(publisher, "This is a test", 0); assertThat(ret, is(14)); ret = ZMQ.send(control, ZMQ.PROXY_TERMINATE, 0); assertThat(ret, is(9)); ZMQ.close(publisher); ZMQ.close(control); resultHander.get(); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/proxy/ProxyTest.java000066400000000000000000000317301455771126300223340ustar00rootroot00000000000000package zmq.proxy; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.nio.channels.Selector; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.poll.PollItem; import zmq.util.Utils; // Asynchronous client-to-server (DEALER to ROUTER) - pure libzmq // // While this example runs in a single process, that is to make // it easier to start and stop the example. Each task may have its own // context and conceptually acts as a separate process. To have this // behaviour, it is necessary to replace the inproc transport of the // control socket by a tcp transport. // This is our client task // It connects to the server, and then sends a request once per second // It collects responses as they arrive, and it prints them out. We will // run several client tasks in parallel, each with a different random ID. public class ProxyTest { private static final class Client implements Callable { private final String host; private final String controlEndpoint; private final boolean verbose; private final CountDownLatch started; Client(String host, String controlEndpoint, boolean verbose) { this.host = host; this.controlEndpoint = controlEndpoint; this.verbose = verbose; this.started = new CountDownLatch(1); } @Override public Boolean call() { Ctx ctx = ZMQ.createContext(); SocketBase client = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(client, notNullValue()); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(control, notNullValue()); boolean rc = ZMQ.setSocketOption(control, ZMQ.ZMQ_SUBSCRIBE, ZMQ.SUBSCRIPTION_ALL); assertThat(rc, is(true)); rc = ZMQ.connect(control, this.controlEndpoint); assertThat(rc, is(true)); String identity = UUID.randomUUID().toString(); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_IDENTITY, identity); assertThat(rc, is(true)); rc = ZMQ.connect(client, host); assertThat(rc, is(true)); PollItem[] items = new PollItem[2]; items[0] = new PollItem(client, ZMQ.ZMQ_POLLIN); items[1] = new PollItem(control, ZMQ.ZMQ_POLLIN); int requestNbr = 0; boolean run = true; Selector selector = ctx.createSelector(); started.countDown(); while (run && ! Thread.currentThread().isInterrupted()) { // Tick once per 200 ms, pulling in arriving messages for (int centitick = 0; centitick < 20; centitick++) { int count = ZMQ.poll(selector, items, 10); if (items[0].isReadable()) { count--; Msg msgrsp = ZMQ.recv(client, 0); String payload = new String(msgrsp.data(), ZMQ.CHARSET); if (verbose) { System.out.printf("%1$s Client received %2$s%n", identity, payload); } // Check that message is still the same assertThat(payload.startsWith(identity + " Request #"), is(true)); int more = ZMQ.getSocketOption(client, ZMQ.ZMQ_RCVMORE); assertThat(more, is(0)); } if (items[1].isReadable()) { count--; Msg msgin = ZMQ.recv(control, 0); if (Arrays.equals(msgin.data(), ZMQ.PROXY_TERMINATE)) { run = false; break; } } assertThat(count, is(0)); } String payload = String.format("%1$s Request #%2$s", identity, ++requestNbr); Msg msgout = new Msg(payload.getBytes(ZMQ.CHARSET)); int sent = ZMQ.send(client, msgout, 0); assertThat(sent, is(msgout.size())); if (verbose) { System.out.printf("%1$s Sent payload %2$s%n", identity, payload); } } ctx.closeSelector(selector); ZMQ.close(control); ZMQ.close(client); ZMQ.term(ctx); return true; } } private static final String BACKEND = "inproc://backend"; private static final class Server implements Callable { private final String host; private final String controlEndpoint; private final boolean verbose; private final CountDownLatch started; Server(String host, String controlEndpoint, boolean verbose) { this.host = host; this.controlEndpoint = controlEndpoint; this.verbose = verbose; this.started = new CountDownLatch(1); } @Override public Boolean call() throws InterruptedException, ExecutionException, TimeoutException { Ctx ctx = ZMQ.createContext(); // Frontend socket talks to clients over TCP SocketBase frontend = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(frontend, notNullValue()); boolean rc = ZMQ.bind(frontend, host); assertThat(rc, is(true)); // Backend socket talks to workers over inproc SocketBase backend = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(backend, notNullValue()); rc = ZMQ.bind(backend, BACKEND); assertThat(rc, is(true)); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(control, notNullValue()); rc = ZMQ.setSocketOption(control, ZMQ.ZMQ_SUBSCRIBE, ZMQ.SUBSCRIPTION_ALL); assertThat(rc, is(true)); rc = ZMQ.connect(control, this.controlEndpoint); assertThat(rc, is(true)); // Launch pool of worker threads, precise number is not critical int count = 5; ExecutorService executor = Executors.newFixedThreadPool(count); List> workers = new ArrayList<>(count); for (int idx = 0; idx < count; ++idx) { Worker w = new Worker(ctx, idx, controlEndpoint, verbose); workers.add(executor.submit(w)); w.started.await(); } started.countDown(); // Connect backend to frontend via a proxy ZMQ.proxy(frontend, backend, null, control); executor.shutdown(); if (!executor.awaitTermination(3, TimeUnit.SECONDS)) { executor.shutdownNow(); fail("Hanged tasks"); } ZMQ.close(frontend); ZMQ.close(backend); ZMQ.close(control); ZMQ.term(ctx); boolean completed = true; for (Future f : workers) { completed &= f.get(3, TimeUnit.SECONDS); } return completed; } } // Each worker task works on one request at a time and sends a random number // of replies back, with random delays between replies: // The comments in the first column, if suppressed, makes it a poller version private static final class Worker implements Callable { private final boolean verbose; private final int idx; private final String control; private final Ctx ctx; private final CountDownLatch started; public Worker(Ctx ctx, int idx, String control, boolean verbose) { this.ctx = ctx; this.idx = idx; this.control = control; this.verbose = verbose; this.started = new CountDownLatch(1); } @Override public Boolean call() { SocketBase worker = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(worker, notNullValue()); boolean rc = ZMQ.connect(worker, BACKEND); assertThat(rc, is(true)); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(control, notNullValue()); rc = ZMQ.setSocketOption(control, ZMQ.ZMQ_SUBSCRIBE, new byte[0]); assertThat(rc, is(true)); rc = ZMQ.connect(control, this.control); assertThat(rc, is(true)); boolean run = true; Random random = new Random(); Msg msg; started.countDown(); while (run) { msg = ZMQ.recv(control, ZMQ.ZMQ_DONTWAIT); if (control.errno() == ZError.ETERM) { break; } if (msg != null) { if (Arrays.equals(msg.data(), ZMQ.PROXY_TERMINATE)) { break; } } // The DEALER socket gives us the reply envelope and message // if we don't poll, we have to use ZMQ_DONTWAIT, if we poll, we can block-receive with 0 Msg identity = ZMQ.recv(worker, ZMQ.ZMQ_DONTWAIT); if (identity != null) { msg = ZMQ.recv(worker, 0); if (verbose) { System.out.printf("Worker #%1$s received %2$s%n", idx, msg); } // Send 0..4 replies back for (int idx = 0; idx < random.nextInt(5); ++idx) { // Sleep for some fraction of a second ZMQ.msleep(random.nextInt(10) + 1); // Send message from server to client int sent = ZMQ.send(worker, identity, ZMQ.ZMQ_SNDMORE); assertThat(sent, is(identity.size())); sent = ZMQ.send(worker, msg, 0); assertThat(sent, is(msg.size())); } } } ZMQ.close(control); ZMQ.close(worker); return true; } } @Test(timeout = 10000) public void testProxy() throws Throwable { // The main thread simply starts several clients and a server, and then // waits for the server to finish. Ctx ctx = ZMQ.createContext(); String controlEndpoint = "tcp://localhost:" + Utils.findOpenPort(); // Control socket receives terminate command from main over inproc SocketBase control = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(control, notNullValue()); boolean rc = ZMQ.bind(control, controlEndpoint); assertThat(rc, is(true)); String host = "tcp://127.0.0.1:" + Utils.findOpenPort(); int count = 5; ExecutorService executor = Executors.newFixedThreadPool(count + 1); Server server = new Server(host, controlEndpoint, false); Future fserver = executor.submit(server); server.started.await(); List> clientsf = new ArrayList<>(count); for (int idx = 0; idx < count; ++idx) { Client client = new Client(host, controlEndpoint, false); clientsf.add(executor.submit(client)); client.started.await(); } while (true) { Thread.sleep(100); int sent = ZMQ.send(control, ZMQ.PROXY_TERMINATE, 0); assertThat(sent, is(9)); if (clientsf.get(4).isDone()) { break; } } for (Future client : clientsf) { try { assertThat(client.get(), is(true)); } catch (ExecutionException e) { e.getCause().printStackTrace(); throw e.getCause(); } } ZMQ.close(control); executor.shutdown(); if (! executor.awaitTermination(4, TimeUnit.SECONDS)) { executor.shutdownNow(); fail("Hanged tasks"); } ZMQ.term(ctx); try { assertThat(fserver.get(), is(true)); } catch (ExecutionException e) { throw e.getCause(); } } } jeromq-0.6.0/src/test/java/zmq/socket/000077500000000000000000000000001455771126300176135ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/AbstractSpecTest.java000066400000000000000000000033131455771126300236740ustar00rootroot00000000000000package zmq.socket; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class AbstractSpecTest { public boolean sendSeq(SocketBase socket, String... data) { int rc = 0; if (data.length == 0) { return false; } for (int idx = 0; idx < data.length; ++idx) { String payload = data[idx]; if (payload == null) { rc = ZMQ.send(socket, new Msg(), idx == data.length - 1 ? 0 : ZMQ.ZMQ_SNDMORE); assertThat(rc < 0, is(false)); } else { Msg msg = new Msg(payload.getBytes(ZMQ.CHARSET)); rc = ZMQ.send(socket, msg, idx == data.length - 1 ? 0 : ZMQ.ZMQ_SNDMORE); assertThat(rc < 0, is(false)); } } return rc >= 0; } public void recvSeq(SocketBase socket, String... data) { for (int idx = 0; idx < data.length; ++idx) { Msg msg = ZMQ.recv(socket, 0); String payload = data[idx]; if (payload == null) { assertThat(msg, notNullValue()); assertThat(msg.size(), is(0)); } else { assertThat(msg, notNullValue()); assertThat(msg.data(), is(payload.getBytes(ZMQ.CHARSET))); } int rc = ZMQ.getSocketOption(socket, ZMQ.ZMQ_RCVMORE); if (idx == data.length - 1) { assertThat(rc, is(0)); } else { assertThat(rc, is(1)); } } } } jeromq-0.6.0/src/test/java/zmq/socket/pair/000077500000000000000000000000001455771126300205465ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/pair/TestPairInproc.java000066400000000000000000000017031455771126300243200ustar00rootroot00000000000000package zmq.socket.pair; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class TestPairInproc { // Create REQ/ROUTER wiring. @Test public void testPairInproc() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(sb, notNullValue()); boolean brc = ZMQ.bind(sb, "inproc://a"); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(sc, notNullValue()); brc = ZMQ.connect(sc, "inproc://a"); assertThat(brc, is(true)); Helper.bounce(sb, sc); // Tear down the wiring. ZMQ.close(sb); ZMQ.close(sc); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pair/TestPairIpc.java000066400000000000000000000022001455771126300235720ustar00rootroot00000000000000package zmq.socket.pair; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.UUID; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class TestPairIpc { // Create REQ/ROUTER wiring. @Test(timeout = 5000) public void testPairIpc() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase pairBind = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(pairBind, notNullValue()); UUID random; do { random = UUID.randomUUID(); } while (!ZMQ.bind(pairBind, "ipc:///tmp/tester/" + random)); SocketBase pairConnect = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(pairConnect, notNullValue()); boolean brc = ZMQ.connect(pairConnect, "ipc:///tmp/tester/" + random); assertThat(brc, is(true)); Helper.bounce(pairBind, pairConnect); // Tear down the wiring. ZMQ.close(pairBind); ZMQ.close(pairConnect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pair/TestPairTcp.java000066400000000000000000000125421455771126300236170ustar00rootroot00000000000000package zmq.socket.pair; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class TestPairTcp { @Test public void testPairTcp() throws IOException { int port = Utils.findOpenPort(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(sb, notNullValue()); boolean brc = ZMQ.bind(sb, "tcp://127.0.0.1:" + port); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(sc, notNullValue()); brc = ZMQ.connect(sc, "tcp://127.0.0.1:" + port); assertThat(brc, is(true)); Helper.bounce(sb, sc); // Tear down the wiring. ZMQ.close(sb); ZMQ.close(sc); ZMQ.term(ctx); } @Test public void testPairConnectSecondClientIssue285() { String host = "tcp://127.0.0.1:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(bind, notNullValue()); boolean brc = ZMQ.bind(bind, host); assertThat(brc, is(true)); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); SocketBase first = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(first, notNullValue()); brc = ZMQ.connect(first, host); assertThat(brc, is(true)); Helper.bounce(bind, first); SocketBase second = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(second, notNullValue()); brc = ZMQ.connect(second, host); assertThat(brc, is(true)); int ret = ZMQ.send(bind, "data", 0); assertThat(ret, is(4)); ret = ZMQ.send(bind, "datb", 0); assertThat(ret, is(4)); ret = ZMQ.send(bind, "datc", 0); assertThat(ret, is(4)); ZMQ.msleep(100); // no receiving from second connected pair Msg msg = ZMQ.recv(second, ZMQ.ZMQ_DONTWAIT); assertThat(msg, nullValue()); // receiving from first connected pair msg = ZMQ.recv(first, ZMQ.ZMQ_DONTWAIT); assertThat(msg, notNullValue()); assertThat(msg.data(), is("data".getBytes(ZMQ.CHARSET))); msg = ZMQ.recv(first, ZMQ.ZMQ_DONTWAIT); assertThat(msg, notNullValue()); assertThat(msg.data(), is("datb".getBytes(ZMQ.CHARSET))); msg = ZMQ.recv(first, ZMQ.ZMQ_DONTWAIT); assertThat(msg, notNullValue()); assertThat(msg.data(), is("datc".getBytes(ZMQ.CHARSET))); // Tear down the wiring. ZMQ.close(bind); ZMQ.close(first); ZMQ.close(second); ZMQ.term(ctx); } @Test public void testPairMonitorBindConnect() throws IOException { int port = Utils.findOpenPort(); String host = "tcp://127.0.0.1:" + port; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(bind, notNullValue()); boolean rc = ZMQ.bind(bind, host); assertThat(rc, is(true)); SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(connect, notNullValue()); rc = ZMQ.connect(connect, host); assertThat(rc, is(true)); Helper.bounce(bind, connect); SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(monitor, notNullValue()); rc = ZMQ.monitorSocket(connect, "inproc://events", ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); rc = ZMQ.bind(monitor, "inproc://events"); assertThat(rc, is(false)); rc = ZMQ.connect(monitor, "inproc://events"); assertThat(rc, is(true)); // Tear down the wiring. ZMQ.close(bind); ZMQ.close(connect); ZMQ.close(monitor); ZMQ.term(ctx); } @Test public void testPairMonitorIssue291() throws IOException { int port = Utils.findOpenPort(); String host = "tcp://127.0.0.1:" + port; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // bind first to use the address SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(bind, notNullValue()); boolean rc = ZMQ.bind(bind, host); assertThat(rc, is(true)); // monitor new socket and connect another pair to send events to SocketBase monitored = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(monitored, notNullValue()); rc = ZMQ.monitorSocket(monitored, "inproc://events", ZMQ.ZMQ_EVENT_ALL); assertThat(rc, is(true)); SocketBase monitor = ZMQ.socket(ctx, ZMQ.ZMQ_PAIR); assertThat(monitor, notNullValue()); rc = ZMQ.connect(monitor, "inproc://events"); assertThat(rc, is(true)); // bind monitored socket with already used address rc = ZMQ.bind(monitored, host); assertThat(rc, is(false)); // Tear down the wiring. ZMQ.close(bind); ZMQ.close(monitored); ZMQ.close(monitor); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pipeline/000077500000000000000000000000001455771126300214205ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/pipeline/PushPullSpecTest.java000066400000000000000000000160501455771126300255140ustar00rootroot00000000000000package zmq.socket.pipeline; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.socket.AbstractSpecTest; public class PushPullSpecTest extends AbstractSpecTest { @Test public void testSpecPullFairQueueIn() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // PULL: SHALL receive incoming messages from its peers using a fair-queuing // strategy. fairQueueIn(ctx, bindAddress, ZMQ.ZMQ_PULL, ZMQ.ZMQ_PUSH); } ZMQ.term(ctx); } @Test public void testSpecPushRoundRobinOut() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // PUSH: SHALL route outgoing messages to connected peers using a // round-robin strategy. roundRobinOut(ctx, bindAddress, ZMQ.ZMQ_PUSH, ZMQ.ZMQ_PULL); } ZMQ.term(ctx); } @Test public void testSpecPushBlockOnSendNoPeers() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // PUSH: SHALL block on sending, or return a suitable error, when it has no // available peers. blockOnSendNoPeers(ctx, bindAddress, ZMQ.ZMQ_PUSH); } ZMQ.term(ctx); } @Test @Ignore public void testSpecDestroyQueueOnDisconnect() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // PUSH and PULL: SHALL create this queue when a peer connects to it. If // this peer disconnects, the socket SHALL destroy its queue and SHALL // discard any messages it contains. // *** Test disabled until libzmq does this properly *** } ZMQ.term(ctx); } private void blockOnSendNoPeers(Ctx ctx, String address, int bindType) { SocketBase push = ZMQ.socket(ctx, bindType); int timeout = 250; boolean rc = ZMQ.setSocketOption(push, ZMQ.ZMQ_SNDTIMEO, timeout); assertThat(rc, is(true)); int ret = ZMQ.send(push, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(push.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(push, "", 0); assertThat(ret, is(-1)); assertThat(push.errno(), is(ZError.EAGAIN)); rc = ZMQ.bind(push, address); assertThat(rc, is(true)); ret = ZMQ.send(push, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(push.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(push, "", 0); assertThat(ret, is(-1)); assertThat(push.errno(), is(ZError.EAGAIN)); ZMQ.close(push); } private void roundRobinOut(Ctx ctx, String address, int bindType, int connectType) { SocketBase push = ZMQ.socket(ctx, bindType); boolean rc = ZMQ.bind(push, address); assertThat(rc, is(true)); int timeout = 250; int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase reps = ZMQ.socket(ctx, connectType); assertThat(reps, notNullValue()); senders.add(reps); rc = ZMQ.setSocketOption(reps, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(push, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(reps, host); assertThat(rc, is(true)); } // Wait for connections. ZMQ.msleep(100); // Send 2N messages for (int peer = 0; peer < services; ++peer) { rc = sendSeq(push, "ABC"); assertThat(rc, is(true)); } for (int peer = 0; peer < services; ++peer) { rc = sendSeq(push, "DEF"); assertThat(rc, is(true)); } // Expect every PULL got one of each for (int peer = 0; peer < services; ++peer) { recvSeq(senders.get(peer), "ABC"); recvSeq(senders.get(peer), "DEF"); } ZMQ.closeZeroLinger(push); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } private void fairQueueIn(Ctx ctx, String address, int bindType, int connectType) { // Server socket will accept connections SocketBase pull = ZMQ.socket(ctx, bindType); assertThat(pull, notNullValue()); boolean rc = ZMQ.bind(pull, address); assertThat(rc, is(true)); int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase sender = ZMQ.socket(ctx, connectType); assertThat(sender, notNullValue()); senders.add(sender); String host = (String) ZMQ.getSocketOptionExt(pull, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(sender, host); assertThat(rc, is(true)); } // Wait for connections. ZMQ.msleep(100); Set firstHalf = new HashSet<>(); Set secondHalf = new HashSet<>(); // Send 2N messages for (int peer = 0; peer < services; ++peer) { sendSeq(senders.get(peer), "A" + peer); firstHalf.add("A" + peer); sendSeq(senders.get(peer), "B" + peer); secondHalf.add("B" + peer); } // Wait for data. ZMQ.msleep(100); // Expect to pull one from each first for (int peer = 0; peer < services; ++peer) { Msg msg = ZMQ.recv(pull, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(2)); firstHalf.remove(new String(msg.data(), ZMQ.CHARSET)); } assertThat(firstHalf.size(), is(0)); // And then get the second batch for (int peer = 0; peer < services; ++peer) { Msg msg = ZMQ.recv(pull, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(2)); secondHalf.remove(new String(msg.data(), ZMQ.CHARSET)); } assertThat(secondHalf.size(), is(0)); ZMQ.closeZeroLinger(pull); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } } jeromq-0.6.0/src/test/java/zmq/socket/pipeline/TestPushPullTcp.java000066400000000000000000000032531455771126300253510ustar00rootroot00000000000000package zmq.socket.pipeline; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class TestPushPullTcp { @Test public void testPushPullTcp() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase push = ZMQ.socket(ctx, ZMQ.ZMQ_PUSH); assertThat(push, notNullValue()); boolean brc = ZMQ.bind(push, "tcp://127.0.0.1:*"); assertThat(brc, is(true)); String host = (String) ZMQ.getSocketOptionExt(push, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); SocketBase pull = ZMQ.socket(ctx, ZMQ.ZMQ_PULL); assertThat(pull, notNullValue()); brc = ZMQ.connect(pull, host); assertThat(brc, is(true)); byte[] content = "12345678ABCDEFGH12345678abcdefgh".getBytes(ZMQ.CHARSET); // Send the message. int rc = ZMQ.send(push, content, 32, ZMQ.ZMQ_SNDMORE); assert (rc == 32); rc = ZMQ.send(push, content, 32, 0); assertThat(rc, is(32)); // Bounce the message back. Msg msg; msg = ZMQ.recv(pull, 0); assert (msg.size() == 32); int rcvmore = ZMQ.getSocketOption(pull, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(1)); msg = ZMQ.recv(pull, 0); assert (rc == 32); rcvmore = ZMQ.getSocketOption(pull, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(0)); // Tear down the wiring. ZMQ.close(push); ZMQ.close(pull); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/000077500000000000000000000000001455771126300211135ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/pubsub/DistTest.java000066400000000000000000000077521455771126300235340ustar00rootroot00000000000000package zmq.socket.pubsub; import org.junit.Before; import org.junit.Test; import zmq.Msg; import zmq.ZObject; import zmq.pipe.Pipe; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class DistTest { private Dist dist; private Pipe first; private Pipe second; private static final class Parent extends ZObject { Parent() { super(null, 0); } } @Before public void setup() { dist = new Dist(); Pipe[] pipes = Pipe.pair(new ZObject[] {new Parent(), new Parent()}, new int[] {0, 1}, new boolean[2]); first = pipes[0]; second = pipes[1]; assertThat(dist.eligible(), is(0)); assertThat(dist.active(), is(0)); dist.attach(first); assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); } @Test public void testActivatedWhileActive() { // check that there is no modification when activating a pipe straight away dist.activated(first); assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); // check re-entrance: no modification dist.activated(first); assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); } @Test public void testAttachedWhenSendingMultipartMessage() { Msg msg = new Msg(); msg.setFlags(Msg.MORE); dist.sendToAll(msg); // no change in eligible/active states assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); dist.attach(second); assertThat(dist.eligible(), is(2)); // active should not have changed, as we are in the middle a sending a multi-part message assertThat(dist.active(), is(1)); // no change in eligible/active states dist.activated(second); assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(1)); dist.sendToAll(new Msg()); // end of multi-part message assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(2)); } @Test public void testAttachedWhenSendingNoMoreMessage() { Msg msg = new Msg(); dist.sendToAll(msg); // no change in eligible/active states assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); dist.attach(second); assertThat(dist.eligible(), is(2)); // active should have changed, as we are NOT in the middle a sending a multi-part message assertThat(dist.active(), is(2)); dist.sendToAll(msg); assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(2)); // no change in eligible/active states dist.activated(second); assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(2)); } @Test public void testActivatedAfterReachingHWM() { dist.attach(second); assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(2)); // simulate HWM second.write(new Msg()); // mark the pipe as not active and not eligible dist.sendToAll(new Msg()); // now, second pipe has been put out of the active and eligible ones assertThat(dist.eligible(), is(1)); assertThat(dist.active(), is(1)); // reactivate pipe having reached HWM dist.activated(second); assertThat(dist.eligible(), is(2)); assertThat(dist.active(), is(2)); } @Test public void testMatch() { dist.attach(second); assertThat(dist.matching(), is(0)); // simulate HWM second.write(new Msg()); // mark the pipe as not active and not eligible dist.sendToAll(new Msg()); dist.match(first); assertThat(dist.matching(), is(1)); dist.match(second); // second pipe is not a matching one assertThat(dist.matching(), is(1)); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/MTrieTest.java000066400000000000000000000161611455771126300236430ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; import org.junit.Test; import zmq.Msg; import zmq.ZObject; import zmq.pipe.Pipe; import zmq.socket.pubsub.Mtrie.IMtrieHandler; public class MTrieTest { private static final class MtrieHandler implements IMtrieHandler { private final AtomicInteger counter = new AtomicInteger(); @Override public void invoke(Pipe pipe, byte[] data, int size, XPub pub) { counter.incrementAndGet(); } } private Pipe pipe; private MtrieHandler handler; private static final Msg prefix = new Msg(new byte[] { 1, 2, 3, 4, 5 }); @Before public void setUp() { pipe = createPipe(); handler = new MtrieHandler(); } private Pipe createPipe() { ZObject object = new ZObject(null, 0) { }; Pipe[] pair = Pipe.pair(new ZObject[] { object, object }, new int[2], new boolean[2]); return pair[0]; } @Test public void testAddRemoveNodeOnTop() { Mtrie mtrie = new Mtrie(); boolean rc = mtrie.addOnTop(pipe); assertThat(rc, is(true)); rc = mtrie.rm(new Msg(1), pipe); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesBelowLevel() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); Pipe third = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 0; Msg above = new Msg(abv); rc = mtrie.add(above, other); assertThat(rc, is(true)); abv[1] = -1; above = new Msg(abv); rc = mtrie.add(above, third); assertThat(rc, is(true)); rc = mtrie.rm(prefix, pipe); assertThat(rc, is(true)); abv[1] = 0; above = new Msg(abv); rc = mtrie.rm(above, other); assertThat(rc, is(true)); abv[1] = -1; above = new Msg(abv); rc = mtrie.rm(above, third); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesAboveLevel() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); Pipe third = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 0; Msg above = new Msg(abv); rc = mtrie.add(above, other); assertThat(rc, is(true)); abv[1] = 33; above = new Msg(abv); rc = mtrie.add(above, third); assertThat(rc, is(true)); rc = mtrie.rm(prefix, pipe); assertThat(rc, is(true)); abv[1] = 0; above = new Msg(abv); rc = mtrie.rm(above, other); assertThat(rc, is(true)); abv[1] = 33; above = new Msg(abv); rc = mtrie.rm(above, third); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesSameLevel() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); rc = mtrie.add(prefix, other); assertThat(rc, is(false)); rc = mtrie.rm(prefix, pipe); assertThat(rc, is(false)); rc = mtrie.rm(prefix, other); assertThat(rc, is(true)); } @Test public void testAddRemoveOneNode() { Mtrie mtrie = new Mtrie(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); rc = mtrie.rm(prefix, pipe); assertThat(rc, is(true)); } @Test public void testAddRemoveOneNodeWithFunctionCall() { Mtrie mtrie = new Mtrie(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); rc = mtrie.rm(pipe, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(1)); } @Test public void testAddRemoveMultiNodesSameLevelWithFunctionCall() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); rc = mtrie.add(prefix, other); assertThat(rc, is(false)); rc = mtrie.rm(pipe, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(0)); rc = mtrie.rm(other, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(1)); } @Test public void testAddRemoveNodeOnTopWithFunctionCall() { Mtrie mtrie = new Mtrie(); boolean rc = mtrie.addOnTop(pipe); assertThat(rc, is(true)); rc = mtrie.rm(pipe, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(1)); } @Test public void testAddRemoveMultiNodesBelowLevelWithFunctionCall() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); Pipe third = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 0; Msg above = new Msg(abv); rc = mtrie.add(above, other); assertThat(rc, is(true)); abv[1] = -1; above = new Msg(abv); rc = mtrie.add(above, third); assertThat(rc, is(true)); rc = mtrie.rm(pipe, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(1)); abv[1] = 0; above = new Msg(abv); rc = mtrie.rm(other, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(2)); abv[1] = -1; above = new Msg(abv); rc = mtrie.rm(third, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(3)); } @Test public void testAddRemoveMultiNodesAboveLevelWithFunctionCall() { Mtrie mtrie = new Mtrie(); Pipe other = createPipe(); Pipe third = createPipe(); boolean rc = mtrie.add(prefix, pipe); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 3; Msg above = new Msg(abv); rc = mtrie.add(above, other); assertThat(rc, is(true)); abv[1] = 33; above = new Msg(abv); rc = mtrie.add(above, third); assertThat(rc, is(true)); rc = mtrie.rm(pipe, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(1)); abv[1] = 3; above = new Msg(abv); rc = mtrie.rm(other, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(2)); abv[1] = 33; above = new Msg(abv); rc = mtrie.rm(third, handler, null); assertThat(rc, is(true)); assertThat(handler.counter.get(), is(3)); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/PubSubHwmTest.java000066400000000000000000000174441455771126300245040ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.util.Wire; public class PubSubHwmTest { @Test public void testDefaults() { // send 1000 msg on hwm 1000, receive 1000 int count = testDefaults(1000, 1000); assertThat(count, is(1000)); } @Test public void testBlocking() { // send 6000 msg on hwm 2000, drops above hwm, only receive hwm int count = testBlocking(2000, 6000); assertThat(count, is(6000)); } private int testDefaults(int sendHwm, int msgCnt) { Ctx ctx = ZMQ.createContext(); // Set up bind socket SocketBase pub = ctx.createSocket(ZMQ.ZMQ_PUB); boolean rc = ZMQ.bind(pub, "inproc://a"); assertThat(rc, is(true)); // Set up connect socket SocketBase sub = ctx.createSocket(ZMQ.ZMQ_SUB); rc = ZMQ.connect(sub, "inproc://a"); assertThat(rc, is(true)); //set a hwm on publisher rc = ZMQ.setSocketOption(pub, ZMQ.ZMQ_SNDHWM, sendHwm); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_SUBSCRIBE, new byte[0]); assertThat(rc, is(true)); // Send until we block int sendCount = 0; while (sendCount < msgCnt && ZMQ.send(pub, "", ZMQ.ZMQ_DONTWAIT) == 0) { ++sendCount; } // Now receive all sent messages int recvCount = 0; while (null != ZMQ.recv(sub, ZMQ.ZMQ_DONTWAIT)) { ++recvCount; } assertThat(sendCount, is(recvCount)); // Clean up ZMQ.close(sub); ZMQ.close(pub); ZMQ.term(ctx); return recvCount; } private int receive(SocketBase socket) { int recvCount = 0; // Now receive all sent messages while (null != ZMQ.recv(socket, ZMQ.ZMQ_DONTWAIT)) { ++recvCount; } return recvCount; } private int testBlocking(int sendHwm, int msgCnt) { Ctx ctx = ZMQ.createContext(); // Set up bind socket SocketBase pub = ctx.createSocket(ZMQ.ZMQ_PUB); boolean rc = ZMQ.bind(pub, "inproc://a"); assertThat(rc, is(true)); // Set up connect socket SocketBase sub = ctx.createSocket(ZMQ.ZMQ_SUB); rc = ZMQ.connect(sub, "inproc://a"); assertThat(rc, is(true)); //set a hwm on publisher rc = ZMQ.setSocketOption(pub, ZMQ.ZMQ_SNDHWM, sendHwm); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(pub, ZMQ.ZMQ_XPUB_NODROP, true); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_SUBSCRIBE, new byte[0]); assertThat(rc, is(true)); // Send until we block int sendCount = 0; int recvCount = 0; while (sendCount < msgCnt) { int ret = ZMQ.send(pub, "", ZMQ.ZMQ_DONTWAIT); if (ret == 0) { ++sendCount; } else if (ret == -1) { assertThat(pub.errno(), is(ZError.EAGAIN)); recvCount += receive(sub); assertThat(sendCount, is(recvCount)); } } recvCount += receive(sub); // Clean up ZMQ.close(sub); ZMQ.close(pub); ZMQ.term(ctx); return recvCount; } @Test public void testResetHwm() { // hwm should apply to the messages that have already been received // with hwm 11024: send 9999 msg, receive 9999, send 1100, receive 1100 int firstCount = 9999; int secondCount = 1100; int hwm = 11024; Ctx ctx = ZMQ.createContext(); // Set up bind socket SocketBase pub = ctx.createSocket(ZMQ.ZMQ_PUB); boolean rc = ZMQ.setSocketOption(pub, ZMQ.ZMQ_SNDHWM, hwm); assertThat(rc, is(true)); rc = ZMQ.bind(pub, "tcp://localhost:*"); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(pub, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connect socket SocketBase sub = ctx.createSocket(ZMQ.ZMQ_SUB); rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_RCVHWM, hwm); assertThat(rc, is(true)); rc = ZMQ.connect(sub, host); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_SUBSCRIBE, new byte[0]); assertThat(rc, is(true)); ZMQ.sleep(1); // Send messages int sendCount = 0; while (sendCount < firstCount && ZMQ.send(pub, "1", ZMQ.ZMQ_DONTWAIT) == 1) { ++sendCount; } assertThat(sendCount, is(firstCount)); ZMQ.msleep(500); // Now receive all sent messages int recvCount = 0; while (null != ZMQ.recv(sub, ZMQ.ZMQ_DONTWAIT)) { ++recvCount; } assertThat(recvCount, is(firstCount)); ZMQ.msleep(100); sendCount = 0; while (sendCount < secondCount && ZMQ.send(pub, "2", ZMQ.ZMQ_DONTWAIT) == 1) { ++sendCount; } assertThat(sendCount, is(secondCount)); ZMQ.msleep(200); // Now receive all sent messages recvCount = 0; while (null != ZMQ.recv(sub, ZMQ.ZMQ_DONTWAIT)) { ++recvCount; } assertThat(recvCount, is(secondCount)); // Clean up ZMQ.close(sub); ZMQ.close(pub); ZMQ.term(ctx); } @Test public void testSubscriptionsHwmTcp() { assertHwmSubscription(999, "tcp://*:*"); } @Test public void testSubscriptionsHwmInproc() { assertHwmSubscription(2000, "inproc://pubsub"); } private void assertHwmSubscription(int subscribed, String endpoint) { int allSubscriptions = 30_000; int subscriptionNotSent = allSubscriptions - 1; Ctx ctx = ZMQ.createContext(); SocketBase pub = ctx.createSocket(ZMQ.ZMQ_PUB); assertThat(pub, notNullValue()); boolean rc = ZMQ.bind(pub, endpoint); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(pub, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connect socket SocketBase sub = ctx.createSocket(ZMQ.ZMQ_SUB); assertThat(sub, notNullValue()); rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_RCVTIMEO, 100); assertThat(rc, is(true)); // send a lot of subscriptions, far beyond the HWM int idx = 0; for (; idx < allSubscriptions; ++idx) { rc = ZMQ.setSocketOption(sub, ZMQ.ZMQ_SUBSCRIBE, Wire.putUInt32(idx)); // at some point, sending will trigger the HWM and subscription will be dropped assertThat(rc, is(true)); } rc = ZMQ.connect(sub, host); assertThat(rc, is(true)); ZMQ.msleep(500); // Send messages int sent = ZMQ.send(pub, Wire.putUInt32(1), 0); assertThat(sent, is(4)); sent = ZMQ.send(pub, Wire.putUInt32(subscribed), 0); assertThat(sent, is(4)); sent = ZMQ.send(pub, Wire.putUInt32(subscriptionNotSent), 0); assertThat(sent, is(4)); ZMQ.msleep(500); Msg msg = ZMQ.recv(sub, 0); assertThat(msg, notNullValue()); assertThat(msg.data(), is(Wire.putUInt32(1))); msg = ZMQ.recv(sub, 0); assertThat(msg, notNullValue()); assertThat(msg.data(), is(Wire.putUInt32(subscribed))); msg = ZMQ.recv(sub, 0); assertThat(msg, nullValue()); // Clean up ZMQ.close(sub); ZMQ.close(pub); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/PubTest.java000066400000000000000000000021711455771126300233450ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Assert; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; public class PubTest { @Test public void testHasIn() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_PUB); int events = pub.getSocketOpt(ZMQ.ZMQ_EVENTS); assertThat(events, is(2)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } @Test public void testRecv() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_PUB); pub.recv(ZMQ.ZMQ_DONTWAIT); Assert.fail("Pub cannot receive message"); } catch (UnsupportedOperationException e) { assertThat(ctx.errno().get(), is(ZError.ENOTSUP)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/SubTest.java000066400000000000000000000032161455771126300233510ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Assert; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; public class SubTest { @Test public void testHasOut() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_SUB); int events = pub.getSocketOpt(ZMQ.ZMQ_EVENTS); assertThat(events, is(0)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } @Test public void testSetNullOption() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_SUB); boolean rc = pub.setSocketOpt(ZMQ.ZMQ_SUBSCRIBE, null); assertThat(rc, is(false)); } catch (IllegalArgumentException e) { assertThat(pub.errno.get(), is(ZError.EINVAL)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } @Test public void testSend() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_SUB); pub.send(new Msg(), ZMQ.ZMQ_DONTWAIT); Assert.fail("Sub cannot send message"); } catch (UnsupportedOperationException e) { assertThat(ctx.errno().get(), is(ZError.ENOTSUP)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/TestPubsubTcp.java000066400000000000000000000036011455771126300245250ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class TestPubsubTcp { @Test public void testPubsubTcp() { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase pubBind = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(pubBind, notNullValue()); ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_XPUB_NODROP, true); boolean rc = ZMQ.bind(pubBind, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(pubBind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); SocketBase subConnect = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(subConnect, notNullValue()); rc = subConnect.setSocketOpt(ZMQ.ZMQ_SUBSCRIBE, "topic"); assertThat(rc, is(true)); rc = ZMQ.connect(subConnect, host); assertThat(rc, is(true)); ZMQ.sleep(1); System.out.print("Send"); rc = pubBind.send(new Msg("topic abc".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); rc = pubBind.send(new Msg("topix defg".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); rc = pubBind.send(new Msg("topic defgh".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); System.out.print(".Recv."); Msg msg = subConnect.recv(0); System.out.print("1."); assertThat(msg.size(), is(9)); msg = subConnect.recv(0); System.out.print("2."); assertThat(msg.size(), is(11)); System.out.print(".End."); ZMQ.close(subConnect); ZMQ.close(pubBind); ZMQ.term(ctx); System.out.println("Done."); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/TestSubForward.java000066400000000000000000000051371455771126300247020ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class TestSubForward { // Create REQ/ROUTER wiring. @Test public void testSubForward() throws IOException { int port1 = Utils.findOpenPort(); int port2 = Utils.findOpenPort(); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // First, create an intermediate device SocketBase xpubBind = ZMQ.socket(ctx, ZMQ.ZMQ_XPUB); assertThat(xpubBind, notNullValue()); boolean rc = ZMQ.bind(xpubBind, "tcp://127.0.0.1:" + port1); SocketBase xsubBind = ZMQ.socket(ctx, ZMQ.ZMQ_XSUB); assertThat(xsubBind, notNullValue()); rc = ZMQ.bind(xsubBind, "tcp://127.0.0.1:" + port2); assertThat(rc, is(true)); // Create a publisher SocketBase pubConnect = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(pubConnect, notNullValue()); rc = ZMQ.connect(pubConnect, "tcp://127.0.0.1:" + port2); assertThat(rc, is(true)); // Create a subscriber SocketBase subConnect = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(subConnect, notNullValue()); rc = ZMQ.connect(subConnect, "tcp://127.0.0.1:" + port1); assertThat(rc, is(true)); // Subscribe for all messages. ZMQ.setSocketOption(subConnect, ZMQ.ZMQ_SUBSCRIBE, ""); ZMQ.sleep(1); // Pass the subscription upstream through the device Msg msg = ZMQ.recv(xpubBind, 0); assertThat(msg, notNullValue()); int n = ZMQ.send(xsubBind, msg, 0); assertThat(n, not(0)); // Wait a bit till the subscription gets to the publisher ZMQ.sleep(1); // Send an empty message n = ZMQ.send(pubConnect, null, 0, 0); assertThat(n, is(0)); // Pass the message downstream through the device msg = ZMQ.recv(xsubBind, 0); assertThat(msg, notNullValue()); n = ZMQ.send(xpubBind, msg, 0); assertThat(n, is(0)); // Receive the message in the subscriber msg = ZMQ.recv(subConnect, 0); assertThat(msg, notNullValue()); // Tear down the wiring. ZMQ.close(xpubBind); ZMQ.close(xsubBind); ZMQ.close(pubConnect); ZMQ.close(subConnect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/TrieTest.java000066400000000000000000000057051455771126300235300ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Test; import zmq.Msg; public class TrieTest { private static final Msg prefix = new Msg(new byte[] { 1, 2, 3, 4, 5 }); @Test public void testAddRemoveNodeOnTop() { Trie trie = new Trie(); boolean rc = trie.add(null, 0, 0); assertThat(rc, is(true)); rc = trie.rm(null, 0, 0); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesBelowLevel() { Trie trie = new Trie(); boolean rc = trie.add(prefix, 0, prefix.size()); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 0; Msg above = new Msg(abv); rc = trie.add(above, 0, above.size()); assertThat(rc, is(true)); abv[1] = -1; above = new Msg(abv); rc = trie.add(above, 0, above.size()); assertThat(rc, is(true)); rc = trie.rm(prefix, 0, prefix.size()); assertThat(rc, is(true)); abv[1] = 0; above = new Msg(abv); rc = trie.rm(above, 0, above.size()); assertThat(rc, is(true)); abv[1] = -1; above = new Msg(abv); rc = trie.rm(above, 0, above.size()); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesAboveLevel() { Trie trie = new Trie(); boolean rc = trie.add(prefix, 0, prefix.size()); assertThat(rc, is(true)); byte[] abv = Arrays.copyOf(prefix.data(), prefix.size()); abv[1] = 3; Msg above = new Msg(abv); rc = trie.add(above, 0, above.size()); assertThat(rc, is(true)); abv[1] = 33; above = new Msg(abv); rc = trie.add(above, 0, above.size()); assertThat(rc, is(true)); rc = trie.rm(prefix, 0, prefix.size()); assertThat(rc, is(true)); abv[1] = 3; above = new Msg(abv); rc = trie.rm(above, 0, above.size()); assertThat(rc, is(true)); abv[1] = 33; above = new Msg(abv); rc = trie.rm(above, 0, above.size()); assertThat(rc, is(true)); } @Test public void testAddRemoveMultiNodesSameLevel() { Trie trie = new Trie(); boolean rc = trie.add(prefix, 0, prefix.size()); assertThat(rc, is(true)); rc = trie.add(prefix, 0, prefix.size()); assertThat(rc, is(false)); rc = trie.rm(prefix, 0, prefix.size()); assertThat(rc, is(false)); rc = trie.rm(prefix, 0, prefix.size()); assertThat(rc, is(true)); } @Test public void testAddRemoveOneNode() { Trie trie = new Trie(); boolean rc = trie.add(prefix, 0, prefix.size()); assertThat(rc, is(true)); rc = trie.rm(prefix, 0, prefix.size()); assertThat(rc, is(true)); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/XPubManualTest.java000066400000000000000000000055061455771126300246400ustar00rootroot00000000000000package zmq.socket.pubsub; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; public class XPubManualTest { @Test public void testXpubManual() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Create a publisher SocketBase pubBind = ZMQ.socket(ctx, ZMQ.ZMQ_XPUB); assertThat(pubBind, notNullValue()); boolean rc = ZMQ.bind(pubBind, "inproc://soname"); assertThat(rc, is(true)); // set pub socket options ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_XPUB_MANUAL, 1); int hwm = 2000; ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_SNDHWM, hwm); // Create a subscriber SocketBase subConnect = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(subConnect, notNullValue()); // Subscribe for "A" messages. ZMQ.setSocketOption(subConnect, ZMQ.ZMQ_SUBSCRIBE, "A"); rc = ZMQ.connect(subConnect, "inproc://soname"); assertThat(rc, is(true)); // Create a subscriber SocketBase subConnect2 = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(subConnect2, notNullValue()); // Subscribe for "B" messages. ZMQ.setSocketOption(subConnect2, ZMQ.ZMQ_SUBSCRIBE, "B"); rc = ZMQ.connect(subConnect2, "inproc://soname"); assertThat(rc, is(true)); Msg subMsg = ZMQ.recv(pubBind, ZMQ.ZMQ_DONTWAIT); ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_SUBSCRIBE, subMsg.data()); subMsg = ZMQ.recv(pubBind, ZMQ.ZMQ_DONTWAIT); ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_SUBSCRIBE, subMsg.data()); int hwmlimit = hwm - 1; int sendCount = 0; // Send message for (int i = 0; i < hwmlimit; i++) { int ret = ZMQ.send(pubBind, "A", 0); assert (ret == 1); ret = ZMQ.send(pubBind, "B", 0); assert (ret == 1); sendCount++; } int recvCount = 0; Msg msg; do { // Receive the message in the subscriber msg = ZMQ.recv(subConnect, ZMQ.ZMQ_DONTWAIT); if (msg != null) { recvCount++; } } while (msg != null); assertThat(sendCount, is(recvCount)); recvCount = 0; do { // Receive the message in the subscriber msg = ZMQ.recv(subConnect2, ZMQ.ZMQ_DONTWAIT); if (msg != null) { recvCount++; } } while (msg != null); assertThat(sendCount, is(recvCount)); // Tear down the wiring. ZMQ.close(pubBind); ZMQ.close(subConnect); ZMQ.close(subConnect2); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/XPubNodropTest.java000066400000000000000000000046171455771126300246660ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; public class XPubNodropTest { // Create REQ/ROUTER wiring. @Test public void testXpubNoDrop() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Create a publisher SocketBase pubBind = ZMQ.socket(ctx, ZMQ.ZMQ_PUB); assertThat(pubBind, notNullValue()); boolean rc = ZMQ.bind(pubBind, "inproc://soname"); assertThat(rc, is(true)); // set pub socket options ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_XPUB_NODROP, 1); int hwm = 2000; ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_SNDHWM, hwm); // Create a subscriber SocketBase subConnect = ZMQ.socket(ctx, ZMQ.ZMQ_SUB); assertThat(subConnect, notNullValue()); rc = ZMQ.connect(subConnect, "inproc://soname"); assertThat(rc, is(true)); // Subscribe for all messages. ZMQ.setSocketOption(subConnect, ZMQ.ZMQ_SUBSCRIBE, ""); int hwmlimit = hwm - 1; int sendCount = 0; // Send an empty message for (int i = 0; i < hwmlimit; i++) { int ret = ZMQ.send(pubBind, "", 0); assert (ret == 0); sendCount++; } int recvCount = 0; Msg msg; do { // Receive the message in the subscriber msg = ZMQ.recv(subConnect, ZMQ.ZMQ_DONTWAIT); if (msg != null) { recvCount++; } } while (msg != null); assertThat(sendCount, is(recvCount)); // Now test real blocking behavior // Set a timeout, default is infinite int timeout = 0; ZMQ.setSocketOption(pubBind, ZMQ.ZMQ_SNDTIMEO, timeout); recvCount = 0; sendCount = 0; while (ZMQ.send(pubBind, "", 0) == 0) { sendCount++; } assertThat(pubBind.errno(), is(ZError.EAGAIN)); while (ZMQ.recv(subConnect, ZMQ.ZMQ_DONTWAIT) != null) { recvCount++; } assertThat(sendCount, is(recvCount)); // Tear down the wiring. ZMQ.close(pubBind); ZMQ.close(subConnect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/XPubTest.java000066400000000000000000000017611455771126300235010ustar00rootroot00000000000000package zmq.socket.pubsub; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZMQ; public class XPubTest { @Test public void testSetVerbose() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_XPUB); boolean rc = pub.setSocketOpt(ZMQ.ZMQ_XPUB_VERBOSE, 0); assertThat(rc, is(true)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } @Test public void testSetNoDrop() { Ctx ctx = ZMQ.createContext(); SocketBase pub = null; try { pub = ctx.createSocket(ZMQ.ZMQ_XPUB); boolean rc = pub.setSocketOpt(ZMQ.ZMQ_XPUB_NODROP, 0); assertThat(rc, is(true)); } finally { ZMQ.close(pub); ZMQ.term(ctx); } } } jeromq-0.6.0/src/test/java/zmq/socket/pubsub/XpubXsubTest.java000066400000000000000000000154571455771126300244120ustar00rootroot00000000000000package zmq.socket.pubsub; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; public class XpubXsubTest { @Test(timeout = 5000) public void testXPubSub() { System.out.println("XPub - Sub"); final Ctx ctx = zmq.ZMQ.createContext(); assertThat(ctx, notNullValue()); boolean rc; SocketBase sub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_SUB); rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_SUBSCRIBE, "topic"); assertThat(rc, is(true)); rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_SUBSCRIBE, "topix"); assertThat(rc, is(true)); SocketBase pub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XPUB); rc = zmq.ZMQ.bind(pub, "inproc://1"); assertThat(rc, is(true)); String endpoint = (String) ZMQ.getSocketOptionExt(pub, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); rc = zmq.ZMQ.connect(sub, endpoint); assertThat(rc, is(true)); System.out.print("Send."); rc = pub.send(new Msg("topic".getBytes(ZMQ.CHARSET)), ZMQ.ZMQ_SNDMORE); assertThat(rc, is(true)); rc = pub.send(new Msg("hop".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); System.out.print("Recv."); Msg msg = sub.recv(0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(5)); msg = sub.recv(0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(3)); rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_UNSUBSCRIBE, "topix"); assertThat(rc, is(true)); rc = pub.send(new Msg("topix".getBytes(ZMQ.CHARSET)), ZMQ.ZMQ_SNDMORE); assertThat(rc, is(true)); rc = pub.send(new Msg("hop".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_RCVTIMEO, 500); assertThat(rc, is(true)); msg = sub.recv(0); assertThat(msg, nullValue()); System.out.print("End."); zmq.ZMQ.close(sub); for (int idx = 0; idx < 2; ++idx) { rc = pub.send(new Msg("topic abc".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); ZMQ.msleep(10); } zmq.ZMQ.close(pub); zmq.ZMQ.term(ctx); System.out.println("Done."); } @Test(timeout = 5000) public void testXPubXSub() { System.out.println("XPub - XSub"); final Ctx ctx = zmq.ZMQ.createContext(); assertThat(ctx, notNullValue()); boolean rc; SocketBase pub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XPUB); rc = zmq.ZMQ.bind(pub, "inproc://1"); assertThat(rc, is(true)); String endpoint = (String) ZMQ.getSocketOptionExt(pub, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(endpoint, notNullValue()); SocketBase sub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XSUB); rc = zmq.ZMQ.connect(sub, endpoint); assertThat(rc, is(true)); System.out.print("Send."); rc = sub.send(new Msg("\1topic".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); rc = pub.send(new Msg("topic".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); System.out.print("Recv."); rc = sub.send(new Msg("\0topic".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); // rc = pub.send(new Msg("topix".getBytes(ZMQ.CHARSET)), ZMQ.ZMQ_SNDMORE); // assertThat(rc, is(true)); // // rc = pub.send(new Msg("hop".getBytes(ZMQ.CHARSET)), 0); // assertThat(rc, is(true)); // // rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_RCVTIMEO, 500); // assertThat(rc, is(true)); // // msg = sub.recv(0); // assertThat(msg, nullValue()); zmq.ZMQ.close(sub); zmq.ZMQ.close(pub); zmq.ZMQ.term(ctx); System.out.println("Done."); } @Test(timeout = 5000) public void testIssue476() throws InterruptedException, ExecutionException { System.out.println("Issue 476"); final Ctx ctx = zmq.ZMQ.createContext(); assertThat(ctx, notNullValue()); boolean rc; final SocketBase proxyPub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XPUB); rc = proxyPub.bind("inproc://1"); assertThat(rc, is(true)); final SocketBase proxySub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XSUB); rc = proxySub.bind("inproc://2"); assertThat(rc, is(true)); final SocketBase ctrl = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_PAIR); rc = ctrl.bind("inproc://ctrl-proxy"); assertThat(rc, is(true)); ExecutorService service = Executors.newFixedThreadPool(1); Future proxy = service.submit(() -> { ZMQ.proxy(proxySub, proxyPub, null, ctrl); }); SocketBase sub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_SUB); rc = zmq.ZMQ.setSocketOption(sub, zmq.ZMQ.ZMQ_SUBSCRIBE, "topic"); assertThat(rc, is(true)); rc = zmq.ZMQ.connect(sub, "inproc://1"); assertThat(rc, is(true)); SocketBase pub = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_XPUB); rc = zmq.ZMQ.connect(pub, "inproc://2"); assertThat(rc, is(true)); rc = zmq.ZMQ.setSocketOption(sub, ZMQ.ZMQ_RCVTIMEO, 100); assertThat(rc, is(true)); sub.recv(0); System.out.print("Send."); rc = pub.send(new Msg("topic".getBytes(ZMQ.CHARSET)), ZMQ.ZMQ_SNDMORE); assertThat(rc, is(true)); rc = pub.send(new Msg("hop".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); System.out.print("Recv."); Msg msg = sub.recv(0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(5)); msg = sub.recv(0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(3)); System.out.print("End."); zmq.ZMQ.close(sub); for (int idx = 0; idx < 2; ++idx) { rc = pub.send(new Msg("topic abc".getBytes(ZMQ.CHARSET)), 0); assertThat(rc, is(true)); ZMQ.msleep(10); } zmq.ZMQ.close(pub); final SocketBase command = zmq.ZMQ.socket(ctx, zmq.ZMQ.ZMQ_PAIR); rc = command.connect("inproc://ctrl-proxy"); assertThat(rc, is(true)); command.send(new Msg(ZMQ.PROXY_TERMINATE), 0); proxy.get(); zmq.ZMQ.close(command); zmq.ZMQ.close(proxyPub); zmq.ZMQ.close(proxySub); zmq.ZMQ.close(ctrl); zmq.ZMQ.term(ctx); System.out.println("Done."); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/000077500000000000000000000000001455771126300211115ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/reqrep/DealerDealerTest.java000066400000000000000000000032161455771126300251270ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class DealerDealerTest { @Test public void testIssue131() throws IOException { Ctx ctx = ZMQ.createContext(); assertThat(ctx, notNullValue()); SocketBase sender = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(sender, notNullValue()); final int port = Utils.findOpenPort(); final String addr = "tcp://localhost:" + port; boolean rc = ZMQ.connect(sender, addr); assertThat(rc, is(true)); byte[] sbuf = msg(255); int sent = ZMQ.send(sender, sbuf, 0); assertThat(sent, is(255)); byte[] quit = { 'q' }; sent = ZMQ.send(sender, quit, 0); assertThat(sent, is(1)); ZMQ.close(sender); SocketBase receiver = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(receiver, notNullValue()); rc = ZMQ.bind(receiver, addr); assertThat(rc, is(true)); int nbytes; do { Msg msg = ZMQ.recv(receiver, 0); nbytes = msg.size(); System.out.println(msg); } while (nbytes != 1); ZMQ.close(receiver); ZMQ.term(ctx); } private byte[] msg(int length) { byte[] msg = new byte[length]; for (int idx = 0; idx < msg.length; ++idx) { msg[idx] = (byte) idx; } return msg; } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/DealerSpecTest.java000066400000000000000000000147421455771126300246330ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Ignore; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.socket.AbstractSpecTest; public class DealerSpecTest extends AbstractSpecTest { @Test public void testSpecFairQueueIn() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL receive incoming messages from its peers using a fair-queuing // strategy. fairQueueIn(ctx, bindAddress, ZMQ.ZMQ_DEALER, ZMQ.ZMQ_DEALER); } ZMQ.term(ctx); } @Test public void testSpecRoundRobinOut() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL route outgoing messages to available peers using a round-robin // strategy. roundRobinOut(ctx, bindAddress, ZMQ.ZMQ_DEALER, ZMQ.ZMQ_REP); } ZMQ.term(ctx); } @Test public void testSpecBlockOnSendNoPeers() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL block on sending, or return a suitable error, when it has no connected peers. blockOnSendNoPeers(ctx, bindAddress, ZMQ.ZMQ_DEALER); } ZMQ.term(ctx); } @Test @Ignore public void testSpecDestroyQueueOnDisconnect() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL create a double queue when a peer connects to it. If this peer // disconnects, the DEALER socket SHALL destroy its double queue and SHALL // discard any messages it contains. // *** Test disabled until libzmq does this properly *** // test_destroy_queue_on_disconnect (ctx); } ZMQ.term(ctx); } private void blockOnSendNoPeers(Ctx ctx, String address, int bindType) { SocketBase dealer = ZMQ.socket(ctx, bindType); int timeout = 250; boolean rc = ZMQ.setSocketOption(dealer, ZMQ.ZMQ_SNDTIMEO, timeout); assertThat(rc, is(true)); int ret = ZMQ.send(dealer, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(dealer.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(dealer, "", 0); assertThat(ret, is(-1)); assertThat(dealer.errno(), is(ZError.EAGAIN)); rc = ZMQ.bind(dealer, address); assertThat(rc, is(true)); ret = ZMQ.send(dealer, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(dealer.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(dealer, "", 0); assertThat(ret, is(-1)); assertThat(dealer.errno(), is(ZError.EAGAIN)); ZMQ.close(dealer); } private void roundRobinOut(Ctx ctx, String address, int bindType, int connectType) { SocketBase dealer = ZMQ.socket(ctx, bindType); boolean rc = ZMQ.bind(dealer, address); assertThat(rc, is(true)); int timeout = 250; int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase reps = ZMQ.socket(ctx, connectType); assertThat(reps, notNullValue()); senders.add(reps); rc = ZMQ.setSocketOption(reps, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(dealer, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(reps, host); assertThat(rc, is(true)); } // Wait for connections. ZMQ.msleep(100); // Send all requests for (int peer = 0; peer < services; ++peer) { rc = sendSeq(dealer, null, "ABC"); assertThat(rc, is(true)); } // Expect every REP got one message for (int peer = 0; peer < services; ++peer) { recvSeq(senders.get(peer), "ABC"); } ZMQ.closeZeroLinger(dealer); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } private void fairQueueIn(Ctx ctx, String address, int bindType, int connectType) { // Server socket will accept connections SocketBase receiver = ZMQ.socket(ctx, bindType); assertThat(receiver, notNullValue()); int timeout = 250; boolean rc = ZMQ.setSocketOption(receiver, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); rc = ZMQ.bind(receiver, address); assertThat(rc, is(true)); int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase sender = ZMQ.socket(ctx, connectType); assertThat(sender, notNullValue()); senders.add(sender); rc = ZMQ.setSocketOption(sender, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(receiver, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(sender, host); assertThat(rc, is(true)); } rc = sendSeq(senders.get(0), "A"); assertThat(rc, is(true)); recvSeq(receiver, "A"); rc = sendSeq(senders.get(0), "A"); assertThat(rc, is(true)); recvSeq(receiver, "A"); // send N requests for (int peer = 0; peer < services; ++peer) { sendSeq(senders.get(peer), "B"); } // Wait for data. ZMQ.msleep(50); // handle N requests for (int peer = 0; peer < services; ++peer) { recvSeq(receiver, "B"); } ZMQ.closeZeroLinger(receiver); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/RepSpecTest.java000066400000000000000000000124101455771126300241530ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.socket.AbstractSpecTest; public class RepSpecTest extends AbstractSpecTest { @Test public void testSpecFairQueueIn() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL receive incoming messages from its peers using a fair-queuing // strategy. fairQueueIn(ctx, bindAddress, ZMQ.ZMQ_REP, ZMQ.ZMQ_REQ); } ZMQ.term(ctx); } @Test public void testSpecEnvelope() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // For an incoming message: // SHALL remove and store the address envelope, including the delimiter. // SHALL pass the remaining data frames to its calling application. // SHALL wait for a single reply message from its calling application. // SHALL prepend the address envelope and delimiter. // SHALL deliver this message back to the originating peer. envelope(ctx, bindAddress, ZMQ.ZMQ_REP, ZMQ.ZMQ_DEALER); } ZMQ.term(ctx); } private void envelope(Ctx ctx, String address, int bindType, int connectType) { SocketBase rep = ZMQ.socket(ctx, bindType); boolean rc = ZMQ.bind(rep, address); assertThat(rc, is(true)); SocketBase dealer = ZMQ.socket(ctx, connectType); assertThat(dealer, notNullValue()); String host = (String) ZMQ.getSocketOptionExt(rep, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(dealer, host); assertThat(rc, is(true)); // minimal envelope sendSeq(dealer, null, "A"); recvSeq(rep, "A"); sendSeq(rep, "A"); recvSeq(dealer, null, "A"); // big envelope sendSeq(dealer, "X", "Y", null, "A"); recvSeq(rep, "A"); sendSeq(rep, "A"); recvSeq(dealer, "X", "Y", null, "A"); ZMQ.closeZeroLinger(rep); ZMQ.closeZeroLinger(dealer); // Wait for disconnects. ZMQ.msleep(100); } private void fairQueueIn(Ctx ctx, String address, int bindType, int connectType) { // Server socket will accept connections SocketBase rep = ZMQ.socket(ctx, bindType); assertThat(rep, notNullValue()); int timeout = 250; boolean rc = ZMQ.setSocketOption(rep, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); rc = ZMQ.bind(rep, address); assertThat(rc, is(true)); int services = 5; List reqs = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase sender = ZMQ.socket(ctx, connectType); assertThat(sender, notNullValue()); reqs.add(sender); rc = ZMQ.setSocketOption(sender, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(rep, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(sender, host); assertThat(rc, is(true)); } rc = sendSeq(reqs.get(0), "A"); assertThat(rc, is(true)); recvSeq(rep, "A"); rc = sendSeq(rep, "A"); assertThat(rc, is(true)); recvSeq(reqs.get(0), "A"); rc = sendSeq(reqs.get(0), "A"); assertThat(rc, is(true)); recvSeq(rep, "A"); rc = sendSeq(rep, "A"); assertThat(rc, is(true)); recvSeq(reqs.get(0), "A"); boolean someoneFixThis = false; // TODO V4 review this test (breaking in libzmq): there is no guarantee about the order of the replies. if (someoneFixThis) { // send N requests for (int peer = 0; peer < services; ++peer) { sendSeq(reqs.get(peer), "B" + peer); } Set replies = new HashSet<>(); // handle N requests for (int peer = 0; peer < services; ++peer) { Msg msg = ZMQ.recv(rep, 0); assertThat(msg, notNullValue()); String reply = new String(msg.data(), ZMQ.CHARSET); replies.add(reply); sendSeq(rep, reply); } for (int peer = 0; peer < services; ++peer) { Msg msg = ZMQ.recv(reqs.get(peer), 0); assertThat(msg, notNullValue()); String reply = new String(msg.data(), ZMQ.CHARSET); replies.remove(reply); } assertThat(replies.size(), is(0)); } ZMQ.closeZeroLinger(rep); for (SocketBase sender : reqs) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/ReqSpecTest.java000066400000000000000000000212101455771126300241520ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Ignore; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; import zmq.socket.AbstractSpecTest; public class ReqSpecTest extends AbstractSpecTest { @Test public void testSpecMessageFormat() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // The request and reply messages SHALL have this format on the wire: // * A delimiter, consisting of an empty frame, added by the REQ socket. // * One or more data frames, comprising the message visible to the // application. messageFormat(ctx, bindAddress, ZMQ.ZMQ_REQ, ZMQ.ZMQ_ROUTER); } ZMQ.term(ctx); } @Test public void testSpecRoundRobinOut() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL route outgoing messages to connected peers using a round-robin // strategy. roundRobinOut(ctx, bindAddress, ZMQ.ZMQ_REQ, ZMQ.ZMQ_REP); } ZMQ.term(ctx); } @Test public void testSpecBlockOnSendNoPeers() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL block on sending, or return a suitable error, when it has no // connected peers. blockOnSendNoPeers(ctx, bindAddress, ZMQ.ZMQ_REQ); } ZMQ.term(ctx); } @Test @Ignore public void testSpecOnlyListensToCurrentPeer() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL accept an incoming message only from the last peer that it sent a // request to. // SHALL discard silently any messages received from other peers. // PH: this test is still failing; disabled for now to allow build to // complete. onlyListensToCurrentPeer(ctx, bindAddress, ZMQ.ZMQ_REQ, ZMQ.ZMQ_ROUTER); } ZMQ.term(ctx); } private void onlyListensToCurrentPeer(Ctx ctx, String bindAddress, int bindType, int connectType) { SocketBase socket = ZMQ.socket(ctx, bindType); boolean rc = ZMQ.setSocketOption(socket, ZMQ.ZMQ_IDENTITY, "A"); assertThat(rc, is(true)); rc = ZMQ.bind(socket, bindAddress); assertThat(rc, is(true)); int timeout = 250; int services = 3; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase connect = ZMQ.socket(ctx, connectType); assertThat(connect, notNullValue()); senders.add(connect); rc = ZMQ.setSocketOption(connect, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(connect, ZMQ.ZMQ_ROUTER_MANDATORY, true); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(socket, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(connect, host); assertThat(rc, is(true)); } ZMQ.msleep(100); for (int peer = 0; peer < services; ++peer) { // There still is a race condition when a stale peer's message // arrives at the REQ just after a request was sent to that peer. // To avoid that happening in the test, sleep for a bit. ZMQ.msleep(10); sendSeq(socket, "ABC"); // Receive on router i recvSeq(senders.get(peer), "A", null, "ABC"); // Send back replies on all routers for (int j = 0; j < services; ++j) { List replies = Arrays.asList("WRONG", "GOOD"); String reply = peer == j ? replies.get(1) : replies.get(0); sendSeq(senders.get(j), "A", null, reply); } // Receive only the good reply recvSeq(socket, "GOOD"); } ZMQ.closeZeroLinger(socket); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } private void blockOnSendNoPeers(Ctx ctx, String address, int bindType) { SocketBase socket = ZMQ.socket(ctx, bindType); int timeout = 250; boolean rc = ZMQ.setSocketOption(socket, ZMQ.ZMQ_SNDTIMEO, timeout); assertThat(rc, is(true)); int ret = ZMQ.send(socket, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(socket.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(socket, "", 0); assertThat(ret, is(-1)); assertThat(socket.errno(), is(ZError.EAGAIN)); rc = ZMQ.bind(socket, address); assertThat(rc, is(true)); ret = ZMQ.send(socket, "", ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(-1)); assertThat(socket.errno(), is(ZError.EAGAIN)); ret = ZMQ.send(socket, "", 0); assertThat(ret, is(-1)); assertThat(socket.errno(), is(ZError.EAGAIN)); ZMQ.close(socket); } private void roundRobinOut(Ctx ctx, String address, int bindType, int connectType) { SocketBase req = ZMQ.socket(ctx, bindType); boolean rc = ZMQ.bind(req, address); assertThat(rc, is(true)); int timeout = 250; int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase reps = ZMQ.socket(ctx, connectType); assertThat(reps, notNullValue()); senders.add(reps); rc = ZMQ.setSocketOption(reps, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(req, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(reps, host); assertThat(rc, is(true)); } // We have to give the connects time to finish otherwise the requests // will not properly round-robin. We could alternatively connect the // REQ sockets to the REP sockets. ZMQ.msleep(200); // Send our peer-replies, and expect every REP it used once in order for (int peer = 0; peer < services; ++peer) { rc = sendSeq(req, "ABC"); assertThat(rc, is(true)); recvSeq(senders.get(peer), "ABC"); rc = sendSeq(senders.get(peer), "DEF"); assertThat(rc, is(true)); recvSeq(req, "DEF"); } ZMQ.closeZeroLinger(req); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } private void messageFormat(Ctx ctx, String address, int bindType, int connectType) { // Server socket will accept connections SocketBase req = ZMQ.socket(ctx, bindType); assertThat(req, notNullValue()); boolean rc = ZMQ.bind(req, address); assertThat(rc, is(true)); SocketBase router = ZMQ.socket(ctx, connectType); assertThat(router, notNullValue()); String host = (String) ZMQ.getSocketOptionExt(req, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(router, host); assertThat(rc, is(true)); // Send a multi-part request. sendSeq(req, "ABC", "DEF"); // Receive peer identity Msg msg = ZMQ.recv(router, 0); assertThat(msg, notNullValue()); assertThat(msg.size() > 0, is(true)); int more = ZMQ.getSocketOption(router, ZMQ.ZMQ_RCVMORE); assertThat(more, is(1)); // Receive the rest. recvSeq(router, null, "ABC", "DEF"); // Send back a single-part reply. int ret = ZMQ.send(router, msg, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(msg.size())); sendSeq(router, null, "GHI"); // Receive reply. recvSeq(req, "GHI"); ZMQ.closeZeroLinger(req); ZMQ.closeZeroLinger(router); // Wait for disconnects. ZMQ.msleep(100); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/RouterHandoverTest.java000066400000000000000000000057061455771126300255730ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class RouterHandoverTest { @Test public void testRouterHandover() { int rc; boolean brc; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase router = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); brc = ZMQ.bind(router, "tcp://127.0.0.1:*"); assertThat(brc, is(true)); // Enable the handover flag ZMQ.setSocketOption(router, ZMQ.ZMQ_ROUTER_HANDOVER, 1); assertThat(router, notNullValue()); // Create dealer called "X" and connect it to our router SocketBase dealerOne = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealerOne, notNullValue()); ZMQ.setSocketOption(dealerOne, ZMQ.ZMQ_IDENTITY, "X"); String host = (String) ZMQ.getSocketOptionExt(router, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); brc = ZMQ.connect(dealerOne, host); assertThat(brc, is(true)); // Get message from dealer to know when connection is ready rc = ZMQ.send(dealerOne, "Hello", 0); assertThat(rc, is(5)); Msg msg = ZMQ.recv(router, 0); assertThat(msg.size(), is(1)); assertThat(new String(msg.data()), is("X")); msg = ZMQ.recv(router, 0); assertThat(msg.size(), is(5)); // Now create a second dealer that uses the same identity SocketBase dealerTwo = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealerTwo, notNullValue()); ZMQ.setSocketOption(dealerTwo, ZMQ.ZMQ_IDENTITY, "X"); brc = ZMQ.connect(dealerTwo, host); assertThat(brc, is(true)); // Get message from dealer to know when connection is ready rc = ZMQ.send(dealerTwo, "Hello", 0); assertThat(rc, is(5)); msg = ZMQ.recv(router, 0); assertThat(msg.size(), is(1)); assertThat(new String(msg.data()), is("X")); msg = ZMQ.recv(router, 0); assertThat(msg.size(), is(5)); // Send a message to 'X' identity. This should be delivered // to the second dealer, instead of the first because of the handover. rc = ZMQ.send(router, "X", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(1)); rc = ZMQ.send(router, "Hello", 0); assertThat(rc, is(5)); // Ensure that the first dealer doesn't receive the message // but the second one does msg = ZMQ.recv(dealerOne, ZMQ.ZMQ_DONTWAIT); assertThat(msg, nullValue()); msg = ZMQ.recv(dealerTwo, 0); assertThat(msg.size(), is(5)); // Clean up. ZMQ.close(router); ZMQ.close(dealerOne); ZMQ.close(dealerTwo); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/RouterMandatoryTest.java000066400000000000000000000123361455771126300257600ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; public class RouterMandatoryTest { @Test public void testRouterMandatory() { int sent; boolean rc; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase router = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); rc = ZMQ.bind(router, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); // Sending a message to an unknown peer with the default setting // This will not report any error sent = ZMQ.send(router, "UNKNOWN", ZMQ.ZMQ_SNDMORE); assertThat(sent, is(7)); sent = ZMQ.send(router, "DATA", 0); assertThat(sent, is(4)); // Send a message to an unknown peer with mandatory routing // This will fail int mandatory = 1; ZMQ.setSocketOption(router, ZMQ.ZMQ_ROUTER_MANDATORY, mandatory); // Send a message and check that it fails sent = ZMQ.send(router, "UNKNOWN", ZMQ.ZMQ_SNDMORE | ZMQ.ZMQ_DONTWAIT); assertThat(sent, is(-1)); assertThat(router.errno(), is(ZError.EHOSTUNREACH)); // Create dealer called "X" and connect it to our router SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); ZMQ.setSocketOption(dealer, ZMQ.ZMQ_IDENTITY, "X"); String host = (String) ZMQ.getSocketOptionExt(router, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(dealer, host); assertThat(rc, is(true)); // Get message from dealer to know when connection is ready int ret = ZMQ.send(dealer, "Hello", 0); assertThat(ret, is(5)); Msg msg = ZMQ.recv(router, 0); assertThat(msg, notNullValue()); assertThat(msg.data()[0], is((byte) 'X')); // Send a message to connected dealer now // It should work sent = ZMQ.send(router, "X", ZMQ.ZMQ_SNDMORE); assertThat(sent, is(1)); sent = ZMQ.send(router, "Hello", 0); assertThat(sent, is(5)); // Clean up. ZMQ.close(router); ZMQ.close(dealer); ZMQ.term(ctx); } private static final int BUF_SIZE = 65636; @Test public void testRouterMandatoryHwm() { boolean rc; System.out.print("Starting router mandatory HWM test"); Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase router = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); ZMQ.setSocketOption(router, ZMQ.ZMQ_ROUTER_MANDATORY, true); ZMQ.setSocketOption(router, ZMQ.ZMQ_SNDHWM, 1); ZMQ.setSocketOption(router, ZMQ.ZMQ_LINGER, 1); rc = ZMQ.bind(router, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); // Create dealer called "X" and connect it to our router, configure HWM SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); ZMQ.setSocketOption(dealer, ZMQ.ZMQ_RCVHWM, 1); ZMQ.setSocketOption(dealer, ZMQ.ZMQ_IDENTITY, "X"); String host = (String) ZMQ.getSocketOptionExt(router, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); rc = ZMQ.connect(dealer, host); assertThat(rc, is(true)); System.out.print("."); // Get message from dealer to know when connection is ready int ret = ZMQ.send(dealer, "Hello", 0); assertThat(ret, is(5)); System.out.print("."); Msg msg = ZMQ.recv(router, 0); System.out.print("."); assertThat(msg, notNullValue()); assertThat(msg.data()[0], is((byte) 'X')); int i = 0; for (; i < 100000; ++i) { ret = ZMQ.send(router, "X", ZMQ.ZMQ_DONTWAIT | ZMQ.ZMQ_SNDMORE); if (ret == -1 && router.errno() == ZError.EAGAIN) { break; } assertThat(ret, is(1)); ret = ZMQ.send(router, new byte[BUF_SIZE], BUF_SIZE, ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(BUF_SIZE)); } System.out.print("."); // This should fail after one message but kernel buffering could // skew results assertThat(i < 10, is(true)); ZMQ.sleep(1); // Send second batch of messages for (; i < 100000; ++i) { ret = ZMQ.send(router, "X", ZMQ.ZMQ_DONTWAIT | ZMQ.ZMQ_SNDMORE); if (ret == -1 && router.errno() == ZError.EAGAIN) { break; } assertThat(ret, is(1)); ret = ZMQ.send(router, new byte[BUF_SIZE], BUF_SIZE, ZMQ.ZMQ_DONTWAIT); assertThat(ret, is(BUF_SIZE)); } System.out.print("."); // This should fail after two messages but kernel buffering could // skew results assertThat(i < 20, is(true)); System.out.println("Done sending messages."); // Clean up. ZMQ.close(router); ZMQ.close(dealer); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/RouterProbeTest.java000066400000000000000000000043361455771126300250720ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class RouterProbeTest { @Test public void testProbeRouter() throws IOException { int port = Utils.findOpenPort(); String host = "tcp://127.0.0.1:" + port; Ctx ctx = ZMQ.createContext(); // Server socket will accept connections SocketBase server = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(server, notNullValue()); boolean rc = ZMQ.bind(server, host); assertThat(rc, is(true)); // Create client and connect to server, doing a probe SocketBase client = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(client, notNullValue()); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_IDENTITY, "X"); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(client, ZMQ.ZMQ_PROBE_ROUTER, true); assertThat(rc, is(true)); rc = ZMQ.connect(client, host); assertThat(rc, is(true)); // We expect an identity=X + empty message from client Msg msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); assertThat(msg.get(0), is((byte) 'X')); msg = ZMQ.recv(server, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(0)); // Send a message to client now int ret = ZMQ.send(server, "X", ZMQ.ZMQ_SNDMORE); assertThat(ret, is(1)); ret = ZMQ.send(server, "Hello", 0); assertThat(ret, is(5)); msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(5)); // TODO DIFF V4 test should stop here, check the logic if we should receive payload in the previous message. msg = ZMQ.recv(client, 0); assertThat(msg, notNullValue()); assertThat(new String(msg.data(), ZMQ.CHARSET), is("Hello")); ZMQ.closeZeroLinger(server); ZMQ.closeZeroLinger(client); // Shutdown ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/RouterSpecTest.java000066400000000000000000000073041455771126300247130ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.socket.AbstractSpecTest; public class RouterSpecTest extends AbstractSpecTest { @Test public void testFairQueueIn() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL receive incoming messages from its peers using a fair-queuing // strategy. fairQueueIn(ctx, bindAddress, ZMQ.ZMQ_ROUTER, ZMQ.ZMQ_DEALER); } ZMQ.term(ctx); } @Test @Ignore public void testDestroyQueueOnDisconnect() { Ctx ctx = ZMQ.createContext(); List binds = Arrays.asList("inproc://a", "tcp://127.0.0.1:*"); for (String bindAddress : binds) { // SHALL create a double queue when a peer connects to it. If this peer // disconnects, the ROUTER socket SHALL destroy its double queue and SHALL // discard any messages it contains. // *** Test disabled until libzmq does this properly *** // test_destroy_queue_on_disconnect (ctx); } ZMQ.term(ctx); } private void fairQueueIn(Ctx ctx, String address, int bindType, int connectType) { // Server socket will accept connections SocketBase receiver = ZMQ.socket(ctx, bindType); assertThat(receiver, notNullValue()); int timeout = 250; boolean rc = ZMQ.setSocketOption(receiver, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); rc = ZMQ.bind(receiver, address); assertThat(rc, is(true)); address = (String) ZMQ.getSocketOptionExt(receiver, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(address, notNullValue()); int services = 5; List senders = new ArrayList<>(); for (int peer = 0; peer < services; ++peer) { SocketBase sender = ZMQ.socket(ctx, connectType); assertThat(sender, notNullValue()); senders.add(sender); rc = ZMQ.setSocketOption(sender, ZMQ.ZMQ_RCVTIMEO, timeout); assertThat(rc, is(true)); rc = ZMQ.setSocketOption(sender, ZMQ.ZMQ_IDENTITY, "A" + peer); assertThat(rc, is(true)); rc = ZMQ.connect(sender, address); assertThat(rc, is(true)); } rc = sendSeq(senders.get(0), "M"); assertThat(rc, is(true)); recvSeq(receiver, "A0", "M"); rc = sendSeq(senders.get(0), "M"); assertThat(rc, is(true)); recvSeq(receiver, "A0", "M"); Set sum = new HashSet<>(); // send N requests for (int peer = 0; peer < services; ++peer) { sendSeq(senders.get(peer), "M"); sum.add("A" + peer); } // handle N requests for (int peer = 0; peer < services; ++peer) { Msg msg = ZMQ.recv(receiver, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(2)); sum.remove(new String(msg.data(), ZMQ.CHARSET)); recvSeq(receiver, "M"); } assertThat(sum.size(), is(0)); ZMQ.closeZeroLinger(receiver); for (SocketBase sender : senders) { ZMQ.closeZeroLinger(sender); } // Wait for disconnects. ZMQ.msleep(100); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestInvalidRep.java000066400000000000000000000044741455771126300246620ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class TestInvalidRep { // Create REQ/ROUTER wiring. @Test public void testInvalidRep() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase routerSocket = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(routerSocket, notNullValue()); SocketBase reqSocket = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(reqSocket, notNullValue()); int linger = 0; int rc; ZMQ.setSocketOption(routerSocket, ZMQ.ZMQ_LINGER, linger); ZMQ.setSocketOption(reqSocket, ZMQ.ZMQ_LINGER, linger); boolean brc = ZMQ.bind(routerSocket, "inproc://hi"); assertThat(brc, is(true)); brc = ZMQ.connect(reqSocket, "inproc://hi"); assertThat(brc, is(true)); // Initial request. rc = ZMQ.send(reqSocket, "r", 0); assertThat(rc, is(1)); // Receive the request. Msg addr; Msg bottom; Msg body; addr = ZMQ.recv(routerSocket, 0); int addrSize = addr.size(); System.out.println("addrSize: " + addr.size()); assertThat(addr.size() > 0, is(true)); bottom = ZMQ.recv(routerSocket, 0); assertThat(bottom.size(), is(0)); body = ZMQ.recv(routerSocket, 0); assertThat(body.size(), is(1)); assertThat(body.data()[0], is((byte) 'r')); // Send invalid reply. rc = ZMQ.send(routerSocket, addr, 0); assertThat(rc, is(addrSize)); // Send valid reply. rc = ZMQ.send(routerSocket, addr, ZMQ.ZMQ_SNDMORE); assertThat(rc, is(addrSize)); rc = ZMQ.send(routerSocket, bottom, ZMQ.ZMQ_SNDMORE); assertThat(rc, is(0)); rc = ZMQ.send(routerSocket, "b", 0); assertThat(rc, is(1)); // Check whether we've got the valid reply. body = ZMQ.recv(reqSocket, 0); assertThat(body.size(), is(1)); assertThat(body.data()[0], is((byte) 'b')); // Tear down the wiring. ZMQ.close(routerSocket); ZMQ.close(reqSocket); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqCorrelateRelaxed.java000066400000000000000000000144621455771126300265200ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; /** * This test does one setup, and runs a few tests on that setup. */ public class TestReqCorrelateRelaxed { static final int REQUEST_ID_LENGTH = 4; private static final String PAYLOAD = "Payload"; /** * Prepares sockets and runs actual tests. *

* Doing it this way so order is guaranteed. * */ @Test public void overallSetup() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); boolean brc = ZMQ.bind(dealer, "inproc://a"); assertThat(brc, is(true)); SocketBase reqClient = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(reqClient, notNullValue()); reqClient.setSocketOpt(ZMQ.ZMQ_REQ_CORRELATE, 1); reqClient.setSocketOpt(ZMQ.ZMQ_REQ_RELAXED, 1); reqClient.setSocketOpt(ZMQ.ZMQ_RCVTIMEO, 100); brc = ZMQ.connect(reqClient, "inproc://a"); assertThat(brc, is(true)); // Test good path byte[] origRequestId = testReqSentFrames(dealer, reqClient); testReqRecvGoodRequestId(dealer, reqClient, origRequestId); // Test what happens when a bad request ID is sent back. origRequestId = testReqSentFrames(dealer, reqClient); testReqRecvBadRequestId(dealer, reqClient, origRequestId); ZMQ.close(reqClient); ZMQ.close(dealer); ZMQ.term(ctx); } /** * Tests that the correct frames are sent by the REQ socket. * * @param dealer * @param reqClient * @return the request ID that was received */ public byte[] testReqSentFrames(SocketBase dealer, SocketBase reqClient) { // Send simple payload over REQ socket Msg request = new Msg(PAYLOAD.getBytes()); assertThat(ZMQ.sendMsg(reqClient, request, 0), is(PAYLOAD.getBytes().length)); // Verify that the right frames were sent: [request ID, empty frame, payload] // 1. request ID Msg receivedReqId = ZMQ.recv(dealer, 0); assertThat(receivedReqId, notNullValue()); assertThat(receivedReqId.size(), is(REQUEST_ID_LENGTH)); assertThat(receivedReqId.flags() & ZMQ.ZMQ_MORE, not(0)); byte[] buf = new byte[128]; int requestIdLen = receivedReqId.getBytes(0, buf, 0, 128); assertThat(requestIdLen, is(REQUEST_ID_LENGTH)); byte[] requestId = Arrays.copyOf(buf, REQUEST_ID_LENGTH); // 2. empty frame Msg receivedEmpty = ZMQ.recv(dealer, 0); assertThat(receivedEmpty, notNullValue()); assertThat(receivedEmpty.size(), is(0)); assertThat(receivedEmpty.flags() & ZMQ.ZMQ_MORE, not(0)); // 3. Payload Msg receivedPayload = ZMQ.recv(dealer, 0); assertThat(receivedPayload, notNullValue()); assertThat(receivedPayload.size(), is(PAYLOAD.getBytes().length)); assertThat(receivedPayload.flags() & ZMQ.ZMQ_MORE, is(0)); int receivedPayloadLen = receivedPayload.getBytes(0, buf, 0, 128); assertThat(receivedPayloadLen, is(PAYLOAD.getBytes().length)); assertThat(Arrays.equals(receivedPayload.data(), PAYLOAD.getBytes()), is(true)); return requestId; } /** * Test that REQ sockets with CORRELATE/RELAXED receive a response with * correct request ID correctly. * * @param dealer * @param reqClient * @param origRequestId the request ID that was sent by the REQ socket * earlier */ public void testReqRecvGoodRequestId(SocketBase dealer, SocketBase reqClient, byte[] origRequestId) { Msg requestId = new Msg(origRequestId); Msg empty = new Msg(); Msg responsePayload = new Msg(PAYLOAD.getBytes()); // Send response assertThat(ZMQ.send(dealer, requestId, ZMQ.ZMQ_SNDMORE), is(REQUEST_ID_LENGTH)); assertThat(ZMQ.send(dealer, empty, ZMQ.ZMQ_SNDMORE), is(0)); assertThat(ZMQ.send(dealer, responsePayload, 0), is(responsePayload.size())); // Receive response (payload only) Msg receivedResponsePayload = ZMQ.recv(reqClient, 0); assertThat(receivedResponsePayload, notNullValue()); byte[] buf = new byte[128]; int payloadLen = receivedResponsePayload.getBytes(0, buf, 0, 128); assertThat(payloadLen, is(PAYLOAD.getBytes().length)); } /** * Asserts that a response with a non-current request ID is ignored. * * @param dealer * @param reqClient * @param origRequestId */ public void testReqRecvBadRequestId(SocketBase dealer, SocketBase reqClient, byte[] origRequestId) { Msg badRequestId = new Msg("gobbledygook".getBytes()); Msg goodRequestId = new Msg(origRequestId); Msg empty = new Msg(); Msg badResponsePayload = new Msg("Bad response".getBytes()); Msg goodResponsePayload = new Msg(PAYLOAD.getBytes()); // Send response with bad request ID assertThat(ZMQ.send(dealer, badRequestId, ZMQ.ZMQ_SNDMORE), is(12)); assertThat(ZMQ.send(dealer, empty, ZMQ.ZMQ_SNDMORE), is(0)); assertThat(ZMQ.send(dealer, badResponsePayload, 0), is(badResponsePayload.size())); // Send response with good request ID assertThat(ZMQ.send(dealer, goodRequestId, ZMQ.ZMQ_SNDMORE), is(REQUEST_ID_LENGTH)); assertThat(ZMQ.send(dealer, empty, ZMQ.ZMQ_SNDMORE), is(0)); assertThat(ZMQ.send(dealer, goodResponsePayload, 0), is(goodResponsePayload.size())); // Receive response (payload only) Msg receivedResponsePayload = ZMQ.recv(reqClient, 0); assertThat(receivedResponsePayload, notNullValue()); // Expecting PAYLOAD, not "Bad payload" byte[] buf = new byte[128]; int payloadLen = receivedResponsePayload.getBytes(0, buf, 0, 128); assertThat(payloadLen, is(PAYLOAD.getBytes().length)); byte[] receivedPayload = Arrays.copyOf(buf, payloadLen); assertThat(receivedPayload, is(PAYLOAD.getBytes())); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqRelaxed.java000066400000000000000000000123661455771126300246600ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.List; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class TestReqRelaxed { @Test public void testReqRelaxed() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase req = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); boolean rc; rc = req.setSocketOpt(ZMQ.ZMQ_REQ_CORRELATE, true); assertThat(rc, is(true)); rc = req.setSocketOpt(ZMQ.ZMQ_REQ_RELAXED, true); assertThat(rc, is(true)); rc = ZMQ.bind(req, "inproc://a"); assertThat(rc, is(true)); int services = 5; List reps = new ArrayList<>(); for (int idx = 0; idx < services; ++idx) { SocketBase rep = ctx.createSocket(ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); reps.add(rep); rc = req.setSocketOpt(ZMQ.ZMQ_RCVTIMEO, 500); assertThat(rc, is(true)); rc = rep.connect("inproc://a"); assertThat(rc, is(true)); } // We have to give the connects time to finish otherwise the requests // will not properly round-robin. We could alternatively connect the // REQ sockets to the REP sockets. ZMQ.msleep(100); // Case 1: Second send() before a reply arrives in a pipe. // Send a request, ensure it arrives, don't send a reply Helper.sendSeq(req, "A", "B"); Helper.recvSeq(reps.get(0), "A", "B"); // Send another request on the REQ socket Helper.sendSeq(req, "C", "D"); Helper.recvSeq(reps.get(1), "C", "D"); // Send a reply to the first request - that should be discarded by the REQ Helper.sendSeq(reps.get(0), "WRONG"); // Send the expected reply Helper.sendSeq(reps.get(1), "OK"); Helper.recvSeq(req, "OK"); // Another standard req-rep cycle, just to check Helper.sendSeq(req, "E"); Helper.recvSeq(reps.get(2), "E"); Helper.sendSeq(reps.get(2), "F", "G"); Helper.recvSeq(req, "F", "G"); // Case 2: Second send() after a reply is already in a pipe on the REQ. // Send a request, ensure it arrives, send a reply Helper.sendSeq(req, "H"); Helper.recvSeq(reps.get(3), "H"); Helper.sendSeq(reps.get(3), "BAD"); // Wait for message to be there. ZMQ.msleep(100); // Without receiving that reply, send another request on the REQ socket Helper.sendSeq(req, "I"); Helper.recvSeq(reps.get(4), "I"); // Send the expected reply Helper.sendSeq(reps.get(4), "GOOD"); Helper.recvSeq(req, "GOOD"); // Case 3: Check issue #1690. Two send() in a row should not close the // communication pipes. For example pipe from req to rep[0] should not be // closed after executing Case 1. So rep[0] should be the next to receive, // not rep[1]. Helper.sendSeq(req, "J"); Helper.recvSeq(reps.get(0), "J"); ZMQ.closeZeroLinger(req); for (SocketBase rep : reps) { ZMQ.closeZeroLinger(rep); } // Wait for disconnects. ZMQ.msleep(100); ZMQ.term(ctx); } @Test public void testIssueLibzmq1965() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase req; boolean rc; // Case 4: Check issue #1695. As messages may pile up before a responder // is available, we check that responses to messages other than the last // sent one are correctly discarded by the REQ pipe // Setup REQ socket as client req = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); rc = req.setSocketOpt(ZMQ.ZMQ_REQ_CORRELATE, true); assertThat(rc, is(true)); rc = req.setSocketOpt(ZMQ.ZMQ_REQ_RELAXED, true); assertThat(rc, is(true)); rc = ZMQ.connect(req, "inproc://b"); assertThat(rc, is(true)); // Setup ROUTER socket as server but do not bind it just yet SocketBase router = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); // Send two requests Helper.sendSeq(req, "TO_BE_DISCARDED"); Helper.sendSeq(req, "TO_BE_ANSWERED"); // Bind server allowing it to receive messages rc = ZMQ.bind(router, "inproc://b"); assertThat(rc, is(true)); // Read the two messages and send them back as is bounce(router); bounce(router); Helper.recvSeq(req, "TO_BE_ANSWERED"); ZMQ.closeZeroLinger(req); ZMQ.closeZeroLinger(router); ctx.terminate(); } private void bounce(SocketBase socket) { boolean more; do { Msg msg = socket.recv(0); assertThat(msg, notNullValue()); more = ZMQ.getSocketOption(socket, ZMQ.ZMQ_RCVMORE) > 0; socket.send(msg, more ? ZMQ.ZMQ_SNDMORE : 0); } while (more); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqrepDevice.java000066400000000000000000000076241455771126300252030ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; import zmq.util.Utils; public class TestReqrepDevice { // Create REQ/ROUTER wiring. @Test public void testReprepDevice() throws IOException { int routerPort = Utils.findOpenPort(); int dealerPort = Utils.findOpenPort(); boolean brc; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Create a req/rep device. SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); brc = ZMQ.bind(dealer, "tcp://127.0.0.1:" + dealerPort); assertThat(brc, is(true)); SocketBase router = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); assertThat(router, notNullValue()); brc = ZMQ.bind(router, "tcp://127.0.0.1:" + routerPort); assertThat(brc, is(true)); // Create a worker. SocketBase rep = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(rep, notNullValue()); brc = ZMQ.connect(rep, "tcp://127.0.0.1:" + dealerPort); assertThat(brc, is(true)); SocketBase req = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(req, notNullValue()); brc = ZMQ.connect(req, "tcp://127.0.0.1:" + routerPort); assertThat(brc, is(true)); // Send a request. int rc; Msg msg; String buff; long rcvmore; rc = ZMQ.send(req, "ABC", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(3)); rc = ZMQ.send(req, "DEFG", 0); assertThat(rc, is(4)); // Pass the request through the device. for (int i = 0; i != 4; i++) { msg = ZMQ.recvMsg(router, 0); assertThat(msg, notNullValue()); rcvmore = ZMQ.getSocketOption(router, ZMQ.ZMQ_RCVMORE); rc = ZMQ.sendMsg(dealer, msg, rcvmore > 0 ? ZMQ.ZMQ_SNDMORE : 0); assertThat(rc >= 0, is(true)); } // Receive the request. msg = ZMQ.recv(rep, 0); assertThat(msg.size(), is(3)); buff = new String(msg.data(), ZMQ.CHARSET); assertThat(buff, is("ABC")); rcvmore = ZMQ.getSocketOption(rep, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore > 0, is(true)); msg = ZMQ.recv(rep, 0); assertThat(msg.size(), is(4)); buff = new String(msg.data(), ZMQ.CHARSET); assertThat(buff, is("DEFG")); rcvmore = ZMQ.getSocketOption(rep, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(0L)); // Send the reply. rc = ZMQ.send(rep, "GHIJKL", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(6)); rc = ZMQ.send(rep, "MN", 0); assertThat(rc, is(2)); // Pass the reply through the device. for (int i = 0; i != 4; i++) { msg = ZMQ.recvMsg(dealer, 0); assertThat(msg, notNullValue()); rcvmore = ZMQ.getSocketOption(dealer, ZMQ.ZMQ_RCVMORE); rc = ZMQ.sendMsg(router, msg, rcvmore > 0 ? ZMQ.ZMQ_SNDMORE : 0); assertThat(rc >= 0, is(true)); } // Receive the reply. msg = ZMQ.recv(req, 0); assertThat(msg.size(), is(6)); buff = new String(msg.data(), ZMQ.CHARSET); assertThat(buff, is("GHIJKL")); rcvmore = ZMQ.getSocketOption(req, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore > 0, is(true)); msg = ZMQ.recv(req, 0); assertThat(msg.size(), is(2)); buff = new String(msg.data(), ZMQ.CHARSET); assertThat(buff, is("MN")); rcvmore = ZMQ.getSocketOption(req, ZMQ.ZMQ_RCVMORE); assertThat(rcvmore, is(0L)); // Clean up. ZMQ.close(req); ZMQ.close(rep); ZMQ.close(router); ZMQ.close(dealer); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqrepInproc.java000066400000000000000000000017071455771126300252320ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class TestReqrepInproc { // Create REQ/ROUTER wiring. @Test public void testReqrepInproc() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(sb, notNullValue()); boolean brc = ZMQ.bind(sb, "inproc://a"); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(sc, notNullValue()); brc = ZMQ.connect(sc, "inproc://a"); assertThat(brc, is(true)); Helper.bounce(sb, sc); // Tear down the wiring. ZMQ.close(sb); ZMQ.close(sc); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqrepIpc.java000066400000000000000000000016561455771126300245160ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class TestReqrepIpc { @Test public void testReqrepIpc() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(sb, notNullValue()); boolean brc = ZMQ.bind(sb, "ipc:///tmp/tester2"); assertThat(brc, is(true)); SocketBase sc = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(sc, notNullValue()); brc = ZMQ.connect(sc, "ipc:///tmp/tester2"); assertThat(brc, is(true)); Helper.bounce(sb, sc); // Tear down the wiring. ZMQ.close(sb); ZMQ.close(sc); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestReqrepTcp.java000066400000000000000000000020741455771126300245240ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Helper; import zmq.SocketBase; import zmq.ZMQ; public class TestReqrepTcp { @Test public void testReqrepTcp() { Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase repBind = ZMQ.socket(ctx, ZMQ.ZMQ_REP); assertThat(repBind, notNullValue()); boolean rc = ZMQ.bind(repBind, "tcp://127.0.0.1:*"); assertThat(rc, is(true)); String host = (String) ZMQ.getSocketOptionExt(repBind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); SocketBase reqConnect = ZMQ.socket(ctx, ZMQ.ZMQ_REQ); assertThat(reqConnect, notNullValue()); rc = ZMQ.connect(reqConnect, host); assertThat(rc, is(true)); Helper.bounce(repBind, reqConnect); ZMQ.close(reqConnect); ZMQ.close(repBind); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/reqrep/TestRouterMandatory.java000066400000000000000000000046621455771126300257630ustar00rootroot00000000000000package zmq.socket.reqrep; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.SocketBase; import zmq.ZError; import zmq.ZMQ; public class TestRouterMandatory { @Test public void testRouterMandatory() throws Exception { int rc; boolean brc; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); SocketBase sa = ZMQ.socket(ctx, ZMQ.ZMQ_ROUTER); ZMQ.setSocketOption(sa, ZMQ.ZMQ_SNDHWM, 1); assertThat(sa, notNullValue()); brc = ZMQ.bind(sa, "tcp://127.0.0.1:*"); assertThat(brc, is(true)); // Sending a message to an unknown peer with the default setting rc = ZMQ.send(sa, "UNKNOWN", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(7)); rc = ZMQ.send(sa, "DATA", 0); assertThat(rc, is(4)); int mandatory = 1; // Set mandatory routing on socket ZMQ.setSocketOption(sa, ZMQ.ZMQ_ROUTER_MANDATORY, mandatory); // Send a message and check that it fails rc = ZMQ.send(sa, "UNKNOWN", ZMQ.ZMQ_SNDMORE | ZMQ.ZMQ_DONTWAIT); assertThat(rc, is(-1)); assertThat(sa.errno(), is(ZError.EHOSTUNREACH)); // Create a valid socket SocketBase sb = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(sb, notNullValue()); ZMQ.setSocketOption(sb, ZMQ.ZMQ_RCVHWM, 1); ZMQ.setSocketOption(sb, ZMQ.ZMQ_IDENTITY, "X"); String host = (String) ZMQ.getSocketOptionExt(sa, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); brc = ZMQ.connect(sb, host); // wait until connect Thread.sleep(1000); // make it full and check that it fails rc = ZMQ.send(sa, "X", ZMQ.ZMQ_SNDMORE); assertThat(rc, is(1)); rc = ZMQ.send(sa, "DATA1", 0); assertThat(rc, is(5)); rc = ZMQ.send(sa, "X", ZMQ.ZMQ_SNDMORE | ZMQ.ZMQ_DONTWAIT); if (rc == 1) { // the first frame has been sent rc = ZMQ.send(sa, "DATA2", 0); assertThat(rc, is(5)); // send more rc = ZMQ.send(sa, "X", ZMQ.ZMQ_SNDMORE | ZMQ.ZMQ_DONTWAIT); } assertThat(rc, is(-1)); assertThat(sa.errno(), is(ZError.EAGAIN)); // Clean up. ZMQ.close(sa); ZMQ.close(sb); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/stream/000077500000000000000000000000001455771126300211065ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/socket/stream/StreamEmptyTest.java000066400000000000000000000030551455771126300250660ustar00rootroot00000000000000package zmq.socket.stream; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class StreamEmptyTest { @Test public void testStreamEmpty() { String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assert (ctx != null); // Set up listener STREAM. SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assert (bind != null); boolean rc = ZMQ.bind(bind, host); assert (rc); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connection stream. SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assert (connect != null); // Do the connection. rc = ZMQ.connect(connect, host); assert (rc); ZMQ.sleep(1); int ret = ZMQ.send(connect, "", 0); assertThat(ret, is(0)); Msg msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(5)); ret = ZMQ.send(bind, msg, ZMQ.ZMQ_SNDMORE); assertThat(ret, is(5)); ret = ZMQ.send(bind, new Msg(), 0); assertThat(ret, is(0)); ZMQ.setSocketOption(bind, ZMQ.ZMQ_LINGER, 0); ZMQ.setSocketOption(connect, ZMQ.ZMQ_LINGER, 0); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/socket/stream/StreamTest.java000066400000000000000000000220611455771126300240450ustar00rootroot00000000000000package zmq.socket.stream; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.junit.Test; import zmq.Ctx; import zmq.Msg; import zmq.SocketBase; import zmq.ZMQ; public class StreamTest { @Test public void testStream2dealer() { final byte[] standardGreeting = new byte[64]; standardGreeting[0] = (byte) 0xff; standardGreeting[8] = 1; standardGreeting[9] = 0x7f; standardGreeting[10] = 3; standardGreeting[12] = 'N'; standardGreeting[13] = 'U'; standardGreeting[14] = 'L'; standardGreeting[15] = 'L'; String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // We'll be using this socket in raw mode SocketBase stream = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assertThat(stream, notNullValue()); boolean rc = ZMQ.setSocketOption(stream, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); rc = ZMQ.bind(stream, host); assertThat(rc, is(true)); host = (String) ZMQ.getSocketOptionExt(stream, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // We'll be using this socket as the other peer SocketBase dealer = ZMQ.socket(ctx, ZMQ.ZMQ_DEALER); assertThat(dealer, notNullValue()); rc = ZMQ.setSocketOption(dealer, ZMQ.ZMQ_LINGER, 0); assertThat(rc, is(true)); rc = ZMQ.connect(dealer, host); assertThat(rc, is(true)); // Send a message on the dealer socket int ret = ZMQ.send(dealer, "Hello", 0); assertThat(ret, is(5)); // Connecting sends a zero message // First frame is identity Msg id = ZMQ.recv(stream, 0); assertThat(id, notNullValue()); assertThat(id.hasMore(), is(true)); // Verify the existence of Peer-Address metadata assertThat(id.getMetadata().get("Peer-Address").startsWith("127.0.0.1:"), is(true)); // Second frame is zero Msg zero = ZMQ.recv(stream, 0); assertThat(zero, notNullValue()); assertThat(zero.size(), is(0)); // Real data follows // First frame is identity id = ZMQ.recv(stream, 0); assertThat(id, notNullValue()); assertThat(id.hasMore(), is(true)); // Verify the existence of Peer-Address metadata assertThat(id.getMetadata().get("Peer-Address").startsWith("127.0.0.1:"), is(true)); // Second frame is greeting signature Msg greeting = ZMQ.recv(stream, 0); assertThat(greeting, notNullValue()); assertThat(greeting.size(), is(10)); assertThat(greeting.data(), is(new byte[] { (byte) 0xff, 0, 0, 0, 0, 0, 0, 0, 1, 0x7f })); // Send our own protocol greeting ret = ZMQ.send(stream, id, ZMQ.ZMQ_SNDMORE); ret = ZMQ.send(stream, standardGreeting, standardGreeting.length, 0); assertThat(ret, is(standardGreeting.length)); // Now we expect the data from the DEALER socket // We want the rest of greeting along with the Ready command int bytesRead = 0; ByteBuffer read = ByteBuffer.allocate(100); while (bytesRead < 97) { // First frame is the identity of the connection (each time) Msg msg = ZMQ.recv(stream, 0); assertThat(msg, notNullValue()); assertThat(msg.hasMore(), is(true)); // Second frame contains the next chunk of data msg = ZMQ.recv(stream, 0); assertThat(msg, notNullValue()); bytesRead += msg.size(); read.put(msg.buf()); } assertThat(read.get(0), is((byte) 3)); assertThat(read.get(1), is((byte) 0)); assertThat(read.get(2), is((byte) 'N')); assertThat(read.get(3), is((byte) 'U')); assertThat(read.get(4), is((byte) 'L')); assertThat(read.get(5), is((byte) 'L')); for (int idx = 0; idx < 16; ++idx) { assertThat(read.get(2 + 4 + idx), is((byte) 0)); } assertThat(read.get(54), is((byte) 4)); assertThat(read.get(55), is((byte) 051)); // octal notation, yes assertThat(read.get(56), is((byte) 5)); assertThat(read.get(57), is((byte) 'R')); assertThat(read.get(58), is((byte) 'E')); assertThat(read.get(59), is((byte) 'A')); assertThat(read.get(60), is((byte) 'D')); assertThat(read.get(61), is((byte) 'Y')); assertThat(read.get(62), is((byte) 013)); assertThat(read.get(63), is((byte) 'S')); assertThat(read.get(64), is((byte) 'o')); assertThat(read.get(65), is((byte) 'c')); assertThat(read.get(66), is((byte) 'k')); assertThat(read.get(67), is((byte) 'e')); assertThat(read.get(68), is((byte) 't')); assertThat(read.get(69), is((byte) '-')); assertThat(read.get(70), is((byte) 'T')); assertThat(read.get(71), is((byte) 'y')); assertThat(read.get(72), is((byte) 'p')); assertThat(read.get(73), is((byte) 'e')); assertThat(read.get(74), is((byte) 0)); assertThat(read.get(75), is((byte) 0)); assertThat(read.get(76), is((byte) 0)); assertThat(read.get(77), is((byte) 6)); assertThat(read.get(78), is((byte) 'D')); assertThat(read.get(79), is((byte) 'E')); assertThat(read.get(80), is((byte) 'A')); assertThat(read.get(81), is((byte) 'L')); assertThat(read.get(82), is((byte) 'E')); assertThat(read.get(83), is((byte) 'R')); assertThat(read.get(84), is((byte) 010)); assertThat(read.get(85), is((byte) 'I')); assertThat(read.get(86), is((byte) 'd')); assertThat(read.get(87), is((byte) 'e')); assertThat(read.get(88), is((byte) 'n')); assertThat(read.get(89), is((byte) 't')); assertThat(read.get(90), is((byte) 'i')); assertThat(read.get(91), is((byte) 't')); assertThat(read.get(92), is((byte) 'y')); assertThat(read.get(93), is((byte) 0)); assertThat(read.get(94), is((byte) 0)); assertThat(read.get(95), is((byte) 0)); assertThat(read.get(96), is((byte) 0)); // Send Ready command ZMQ.send(stream, id, ZMQ.ZMQ_SNDMORE); ZMQ.send( stream, new byte[] { 4, 41, 5, 'R', 'E', 'A', 'D', 'Y', 11, 'S', 'o', 'c', 'k', 'e', 't', '-', 'T', 'y', 'p', 'e', 0, 0, 0, 6, 'R', 'O', 'U', 'T', 'E', 'R', 8, 'I', 'd', 'e', 'n', 't', 'i', 't', 'y', 0, 0, 0, 0 }, 0); // Now we expect the data from the DEALER socket // First frame is, again, the identity of the connection id = ZMQ.recv(stream, 0); assertThat(id, notNullValue()); assertThat(id.hasMore(), is(true)); // Third frame contains Hello message from DEALER Msg un = ZMQ.recv(stream, 0); assertThat(un, notNullValue()); assertThat(un.size(), is(7)); // Then we have a 5-byte message "Hello" assertThat(un.get(0), is((byte) 0)); assertThat(un.get(1), is((byte) 5)); assertThat(un.get(2), is((byte) 'H')); assertThat(un.get(3), is((byte) 'e')); assertThat(un.get(4), is((byte) 'l')); assertThat(un.get(5), is((byte) 'l')); assertThat(un.get(6), is((byte) 'o')); // Send "World" back to DEALER ZMQ.send(stream, id, ZMQ.ZMQ_SNDMORE); ret = ZMQ.send(stream, new byte[] { 0, 5, 'W', 'o', 'r', 'l', 'd' }, 0); assertThat(ret, is(7)); // Expect response on DEALER socket Msg recv = ZMQ.recv(dealer, 0); assertThat(recv.size(), is(5)); assertThat(recv.data(), is("World".getBytes(ZMQ.CHARSET))); ZMQ.close(stream); ZMQ.close(dealer); ZMQ.term(ctx); } @Test public void testStream2stream() { String host = "tcp://localhost:*"; Ctx ctx = ZMQ.init(1); assertThat(ctx, notNullValue()); // Set up listener STREAM. SocketBase bind = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assertThat(bind, notNullValue()); boolean rc = ZMQ.bind(bind, host); assertThat(rc, is(true)); host = (String) ZMQ.getSocketOptionExt(bind, ZMQ.ZMQ_LAST_ENDPOINT); assertThat(host, notNullValue()); // Set up connection stream. SocketBase connect = ZMQ.socket(ctx, ZMQ.ZMQ_STREAM); assertThat(connect, notNullValue()); // Do the connection. rc = ZMQ.connect(connect, host); assertThat(rc, is(true)); ZMQ.sleep(1); // Connecting sends a zero message // Server: First frame is identity, second frame is zero Msg msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(msg.size() > 0, is(true)); msg = ZMQ.recv(bind, 0); assertThat(msg, notNullValue()); assertThat(msg.size(), is(0)); ZMQ.close(bind); ZMQ.close(connect); ZMQ.term(ctx); } } jeromq-0.6.0/src/test/java/zmq/util/000077500000000000000000000000001455771126300173005ustar00rootroot00000000000000jeromq-0.6.0/src/test/java/zmq/util/AndroidProblematic.java000066400000000000000000000000741455771126300237060ustar00rootroot00000000000000package zmq.util; public @interface AndroidProblematic { } jeromq-0.6.0/src/test/java/zmq/util/MultimapTest.java000066400000000000000000000041071455771126300225750ustar00rootroot00000000000000package zmq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Collection; import java.util.Collections; import org.junit.Before; import org.junit.Test; public class MultimapTest { private MultiMap map; @Before public void setup() { map = new MultiMap<>(); final Integer value = 42; boolean rc = map.insert(1L, value); assertThat(rc, is(true)); } @Test public void testInsertTwice() { boolean rc = map.insert(1L, 42); assertThat(rc, is(true)); assertThat(map.entries().size(), is(1)); } @Test public void testKey() { Long key = map.key(42); assertThat(key, is(1L)); } @Test public void testContains() { assertThat(map.contains(42), is(true)); assertSize(1); } @Test public void testEmpty() { assertThat(map.isEmpty(), is(false)); } @Test public void testClear() { map.clear(); assertSize(0); } @Test public void testHasValue() { assertThat(map.hasValues(1L), is(true)); assertThat(map.hasValues(42L), is(false)); } @Test public void testRemoveValue() { boolean rc = map.remove(42); assertThat(rc, is(true)); assertSize(0); } @Test public void testRemoveWrongValue() { boolean rc = map.remove(41); assertThat(rc, is(false)); assertSize(1); } @Test public void testRemoveKey() { Collection removed = map.remove(1L); assertThat(removed, is(Collections.singletonList(42))); assertSize(0); } @Test public void testRemoveWrongKey() { Collection removed = map.remove(1L); assertThat(removed, is(Collections.singletonList(42))); assertSize(0); } private void assertSize(int size) { assertThat(map.entries().size(), is(size)); assertThat(map.isEmpty(), is(size == 0)); } } jeromq-0.6.0/src/test/java/zmq/util/TestBlob.java000066400000000000000000000010451455771126300216610ustar00rootroot00000000000000package zmq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.HashMap; import org.junit.Test; import zmq.ZMQ; public class TestBlob { @Test public void testBlobMap() { HashMap map = new HashMap<>(); Blob b = Blob.createBlob("a".getBytes(ZMQ.CHARSET)); map.put(b, "aa"); assertThat(map.remove(b), notNullValue()); assertThat(map.size(), is(0)); } } jeromq-0.6.0/src/test/java/zmq/util/TestUtils.java000066400000000000000000000073331455771126300221110ustar00rootroot00000000000000package zmq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import org.junit.Test; import org.zeromq.TemporaryFolderFinder; public class TestUtils { public static int port(String endpoint) { return Integer.parseInt(endpoint.substring(endpoint.lastIndexOf(':') + 1)); } @Test public void testUnhash() { // theoretically up to 65535 but let's be greedy and waste 10 ms for (int port = 0; port < 100_000; ++port) { String hash = Utils.unhash(port); assertThat(hash, notNullValue()); assertThat(hash.hashCode(), is(port)); } } @Test public void testRealloc() { Integer[] src = new Integer[] { 1, 3, 5 }; Integer[] dest = Utils.realloc(Integer.class, src, 3, true); assertThat(src.length, is(3)); assertThat(src, is(dest)); dest = Utils.realloc(Integer.class, src, 5, true); assertThat(dest.length, is(5)); assertThat(dest[0], is(1)); assertThat(dest[1], is(3)); assertThat(dest[2], is(5)); assertThat(dest[4], nullValue()); dest = Utils.realloc(Integer.class, src, 6, false); assertThat(dest.length, is(6)); assertThat(dest[0], nullValue()); assertThat(dest[1], nullValue()); assertThat(dest[2], nullValue()); assertThat(dest[3], is(1)); assertThat(dest[4], is(3)); assertThat(dest[5], is(5)); src = new Integer[] { 1, 3, 5, 7, 9, 11 }; dest = Utils.realloc(Integer.class, src, 4, false); assertThat(dest.length, is(4)); assertThat(dest[0], is(1)); assertThat(dest[1], is(3)); assertThat(dest[2], is(5)); assertThat(dest[3], is(7)); dest = Utils.realloc(Integer.class, src, 3, true); assertThat(dest.length, is(3)); assertThat(dest[0], is(7)); assertThat(dest[1], is(9)); assertThat(dest[2], is(11)); } @Test public void testDeleteFile() throws IOException { File dir = new File(TemporaryFolderFinder.resolve("testfile")); dir.mkdirs(); File path = File.createTempFile("test", "suffix", dir); assertThat(path.exists(), is(true)); Utils.delete(dir); assertThat(path.exists(), is(false)); } @Test public void testDeleteDir() throws IOException { File dir = new File(TemporaryFolderFinder.resolve("testdir")); dir.mkdirs(); File path = File.createTempFile("test", "suffix", dir); Utils.delete(dir); assertThat(dir.exists(), is(false)); assertThat(path.exists(), is(false)); } @Test public void testDump() { byte[] array = new byte[10]; ByteBuffer buffer = ByteBuffer.wrap(array); String dump = Utils.dump(buffer, 0, 10); assertThat(dump, is("[0,0,0,0,0,0,0,0,0,0,]")); } @Test public void testBytes() { byte[] array = new byte[10]; ByteBuffer buffer = ByteBuffer.wrap(array); byte[] bytes = Utils.bytes(buffer); assertThat(bytes, is(array)); } @Test public void testCheckingCorrectArgument() { try { Utils.checkArgument(true, "Error"); } catch (Throwable t) { fail("Checking argument should not fail"); } } @Test(expected = IllegalArgumentException.class) public void testCheckingIncorrectArgument() { Utils.checkArgument(false, "Error"); } } jeromq-0.6.0/src/test/java/zmq/util/TimersTest.java000066400000000000000000000145431455771126300222550ustar00rootroot00000000000000package zmq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.junit.Before; import org.junit.Test; import zmq.ZMQ; import zmq.util.Timers.Timer; public class TimersTest { private Timers timers; private AtomicBoolean invoked = new AtomicBoolean(); private final Timers.Handler handler = args -> { AtomicBoolean invoked = (AtomicBoolean) args[0]; invoked.set(true); }; @Before public void setup() { timers = new Timers(); invoked = new AtomicBoolean(); } @Test public void testAddFaultyHandler() { Timers.Timer timer = timers.add(10, null); assertThat(timer, nullValue()); } @Test public void testCancelTwice() { Timers.Timer timer = timers.add(10, handler); assertThat(timer, notNullValue()); boolean rc = timer.cancel(); assertThat(rc, is(true)); rc = timer.cancel(); assertThat(rc, is(false)); } @Test public void testTimeoutNoActiveTimers() { long timeout = timers.timeout(); assertThat(timeout, is(-1L)); } @Test public void testNotInvokedInitial() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Timer should not have been invoked yet int rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testNotInvokedHalfTime() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Wait half the time and check again long timeout = timers.timeout(); ZMQ.msleep(timeout / 2); int rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testInvoked() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Wait until the end timers.sleepAndExecute(); assertThat(invoked.get(), is(true)); } @Test public void testNotInvokedAfterHalfTimeAgain() { long fullTimeout = 100; timers.add(fullTimeout, handler, invoked); // Wait until the end int rc = timers.sleepAndExecute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(true)); // Wait half the time and check again long timeout = timers.timeout(); ZMQ.msleep(timeout / 2); rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testNotInvokedAfterResetHalfTime() { long fullTimeout = 100; Timers.Timer timer = timers.add(fullTimeout, handler, invoked); // Wait half the time and check again long timeout = timers.timeout(); ZMQ.msleep(timeout / 2); int rc = timers.execute(); assertThat(rc, is(0)); // Reset timer and wait half of the time left boolean ret = timer.reset(); assertThat(ret, is(true)); ZMQ.msleep(timeout / 2); rc = timers.execute(); assertThat(rc, is(0)); } @Test public void testInvokedAfterReset() { testNotInvokedAfterResetHalfTime(); // Wait until the end int rc = timers.sleepAndExecute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(true)); } @Test public void testReschedule() { long fullTimeout = 100; Timers.Timer timer = timers.add(fullTimeout, handler, invoked); // reschedule boolean ret = timer.setInterval(50); assertThat(ret, is(true)); int rc = timers.sleepAndExecute(); assertThat(rc, is(1)); assertThat(invoked.get(), is(true)); } @Test public void testCancel() { long fullTimeout = 100; Timers.Timer timer = timers.add(fullTimeout, handler, invoked); // cancel timer long timeout = timers.timeout(); boolean ret = timer.cancel(); assertThat(ret, is(true)); ZMQ.msleep(timeout * 2); int rc = timers.execute(); assertThat(rc, is(0)); assertThat(invoked.get(), is(false)); } @Test public void testTimerOrder() { final AtomicLong time = new AtomicLong(); Timers timer = new Timers(time::get); Timer timer100 = timer.add(100, handler, invoked); Timer timer1000 = timer.add(1000, handler, invoked); Timer timer10 = timer.add(10, handler, invoked); Iterator> entries = timer.entries().iterator(); // entries are sorted by order of execution assertThat(entries.next().getKey(), is(timer10)); assertThat(entries.next().getKey(), is(timer100)); assertThat(entries.next().getKey(), is(timer1000)); assertThat(entries.hasNext(), is(false)); timer10.cancel(); // cancel did not touch the order entries = timer.entries().iterator(); assertThat(entries.next().getKey(), is(timer10)); assertThat(entries.next().getKey(), is(timer100)); assertThat(entries.next().getKey(), is(timer1000)); assertThat(entries.hasNext(), is(false)); int rc = timer.execute(); // execute deleted the cancelled timers entries = timer.entries().iterator(); assertThat(rc, is(0)); assertThat(entries.next().getKey(), is(timer100)); assertThat(entries.next().getKey(), is(timer1000)); assertThat(entries.hasNext(), is(false)); timer1000.cancel(); rc = timer.execute(); // execute did not delete the cancelled timers that are after the next one to be executed entries = timer.entries().iterator(); assertThat(rc, is(0)); assertThat(entries.next().getKey(), is(timer100)); assertThat(entries.next().getKey(), is(timer1000)); assertThat(entries.hasNext(), is(false)); time.set(101); rc = timer.execute(); // execute did delete the cancelled timer entries = timer.entries().iterator(); assertThat(rc, is(1)); assertThat(entries.next().getKey(), is(timer100)); assertThat(entries.hasNext(), is(false)); } } jeromq-0.6.0/src/test/java/zmq/util/WireTest.java000066400000000000000000000076751455771126300217300ustar00rootroot00000000000000package zmq.util; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import java.util.Arrays; import org.junit.Test; public class WireTest { @Test public void testUint16() { testUint16(0); testUint16(0xff); testUint16(0xff + 1); testUint16(0xff - 1); testUint16(0xffff - 1); testUint16(0xffff); } @Test public void testUint32() { testUint32(0); testUint32(0xff); testUint32(0xff + 1); testUint32(0xff - 1); testUint32(811233478); testUint32(12345678); testUint32(2048 * 2000); testUint32(2048 * 20000); testUint32(2048 * 2048 * 2048); testUint32(2048 * 2048 * 2048 * 2048); testUint32(2048 * 2048 * 2048 * 2048 * 2048); testUint32(2048 * 2048 * 2048 * 2048 * 2048 * 2048); testUint32(2048 * 2048 * 2048 * 2048 * 2048 * 2048 * 2048); testUint32(2048 * 2048 * 2048 * 2048 * 2048 * 2048 * 2048 * 2048); testUint32(Integer.MAX_VALUE); testUint32(-811233478); testUint32(Integer.MIN_VALUE); } private void testUint32(int expected) { ByteBuffer buf = ByteBuffer.allocate(8); Wire.putUInt32(buf, expected); int actual = Wire.getUInt32(buf); assertThat(actual, is(expected)); } private void testUint16(int expected) { byte[] buf = Wire.putUInt16(expected); int actual = Wire.getUInt16(buf); assertThat(actual, is(expected)); } @Test public void testUnsignedInteger64() { testUnsignedLong(0); testUnsignedLong(0xff); testUnsignedLong(0xff + 1); testUnsignedLong(0xff - 1); testUnsignedLong(811233478); testUnsignedLong(12345678); testUnsignedLong(2048 * 2000); testUnsignedLong(2048 * 20000); testUnsignedLong(2048 * 2048 * 2048); testUnsignedLong(2048 * 2048 * 2048 * 2048); testUnsignedLong(Integer.MAX_VALUE); testUnsignedLong(Long.MAX_VALUE); testUnsignedLong(-1); testUnsignedLong(-811233478); testUnsignedLong(Integer.MIN_VALUE); testUnsignedLong(Long.MIN_VALUE); } private void testUnsignedLong(long expected) { ByteBuffer buf = ByteBuffer.allocate(8); Wire.putUInt64(buf, expected); long actual = Wire.getUInt64(buf, 0); assertThat(actual, is(expected)); } @Test public void testShortString() { testShortString("abcdef", 0); testShortString("ghijklm", 3); } @Test public void testShortStringOutOfByteRange() { testShortString(string(Byte.MAX_VALUE + 1, 'C'), 0); } @Test public void testShortStringMax() { testShortString(string(255, 'C'), 0); } @Test(expected = IllegalArgumentException.class) public void testShortStringTooLong() { testShortString(string(256, 'B'), 0); } @Test public void testLongString() { testLongString("abcdef", 1); testLongString(string(256, 'D'), 0); } private void testShortString(String expected, int offset) { ByteBuffer buf = ByteBuffer.allocate(expected.length() + 1 + offset); buf.position(offset); Wire.putShortString(buf, expected); String actual = Wire.getShortString(buf, offset); assertThat(actual, is(expected)); } private void testLongString(String expected, int offset) { ByteBuffer buf = ByteBuffer.allocate(expected.length() + 4 + offset); buf.position(offset); Wire.putLongString(buf, expected); String actual = Wire.getLongString(buf, offset); assertThat(actual, is(expected)); } private String string(int length, char letter) { byte[] content = new byte[length]; Arrays.fill(content, (byte) letter); return new String(content); } }