/* * Copyright (c) 2011-2015 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ /** * = Vert.x-Stomp * :toc: left * * STOMP is the Simple (or Streaming) Text Orientated Messaging Protocol. STOMP * provides an interoperable wire format so that STOMP clients can communicate with any STOMP message broker to * provide easy and widespread messaging interoperability among many languages, platforms and brokers. Get more details about STOMP on https://stomp.github.io/index.html. * * Vertx-Stomp is an implementation of a STOMP server and client. You can use the STOMP server with other clients and * use the STOMP client with other servers. The server and the client supports the version 1.0, 1.1 and 1.2 of the * STOMP protocol (see https://stomp.github.io/stomp-specification-1.2.html). The STOMP server can also be used as a * bridge with the vert.x event bus, or directly with web sockets (using StompJS). * * == Using vertx-stomp * * To use the Vert.x Stomp server and client, add the following dependency to the _dependencies_ section of your build * descriptor: * * * Maven (in your `pom.xml`): * * [source,xml,subs="+attributes"] * ---- * <dependency> * <groupId>${maven.groupId}</groupId> * <artifactId>${maven.artifactId}</artifactId> * <version>${maven.version}</version> * </dependency> * ---- * * * Gradle (in your `build.gradle` file): * * [source,groovy,subs="+attributes"] * ---- * compile '${maven.groupId}:${maven.artifactId}:${maven.version}' * ---- * * == STOMP server * * === Creating a STOMP server * * The simplest way to create an STOMP server, using all default options is as follows: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example1} * ---- * * This creates a STOMP server listening on `localhost:61613` that is compliant with the STOMP specification. * * You can configure the port and host in the {@link io.vertx.ext.stomp.StompServer#listen(int, java.lang.String)} * method: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example2} * ---- * * If you pass {@code -1} as port, the TCP server would not be started. This is useful when using the websocket * bridge. To be notified when the server is ready, use a handler as follows: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example3} * ---- * * The handler receive a reference on the {@link io.vertx.ext.stomp.StompServer}. * * You can also configure the host and port in {@link io.vertx.ext.stomp.StompServerOptions}: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example4} * ---- * * === Closing a STOMP server * * STOMP servers are closed as follows: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example10} * ---- * * === Configuration * * The {@link io.vertx.ext.stomp.StompServerOptions} let you configure some aspects of the STOMP server. * * First, the STOMP server is based on a * {@link io.vertx.core.net.NetServer}, so you can configure the underlying {@link io.vertx.core.net.NetServer} from * the {@link io.vertx.ext.stomp.StompServerOptions}. Alternatively you can also pass the * {@link io.vertx.core.net.NetServer} you want to use: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example5} * ---- * * The {@link io.vertx.ext.stomp.StompServerOptions} let you configure: * * * the host and port of the STOMP server - defaults to `0.0.0.0:61613`. * * whether or not the STOMP server is secured - defaults to `false` * * the max STOMP frame body - default to 10 Mb * * the maximum number of headers accepted in a STOMP frame - defaults to 1000 * * the max length of a header line in a STOMP frame - defaults to 10240 * * the STOMP heartbeat time - default to `1000, 1000` * * the supported STOMP protocol versions (1.0, 1.1 and 1.2 by default) * * the maximum number of frame allowed in a transaction (defaults to 1000) * * the size of the transaction chunk - defaults to 1000 (see * {@link io.vertx.ext.stomp.StompServerOptions#setTransactionChunkSize(int)}) * * the maximum number of subscriptions a client can handle - defaults to 1000 * * The STOMP heartbeat is configured using a JSON object as follows: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example6} * ---- * * Enabling security requires an additional {@link io.vertx.ext.auth.AuthProvider} handling the * authentication requests: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example7} * ---- * * More information about {@link io.vertx.ext.auth.AuthProvider} is available * http://vertx.io/docs/#authentication_and_authorisation[here]. * * If a frame exceeds one of the size limits, the frame is rejected and the client receives an `ERROR` frame. As the * specification requires, the client connection is closed immediately after having sent the error. The same behavior * happens with the other thresholds. * * === Subscriptions * * The default STOMP server handles subscription destination as opaque Strings. So it does not promote a structure * and it not hierarchic. By default the STOMP server follow a _topic_ semantic (so messages are dispatched to all * subscribers). * * === Type of destinations * * By default, the STOMP server manages _destinations_ as topics. So messages are dispatched to all subscribers. You * can configure the server to use queues, or mix both types: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example11} * ---- * * In the last example, all destination starting with `/queue` are queues while others are topics. The destination is * created when the first subscription on this destination is received. * * A server can decide to reject the destination creation by returning `null`: * *[source,$lang] * ---- * {@link examples.StompServerExamples#example12} * ---- * * In this case, the subscriber received an `ERROR` frame. * * Queues dispatches messages using a round-robin strategies. * * === Providing your own type of destination * * On purpose the STOMP server does not implement any advanced feature. IF you need more advanced dispatching policy, * you can implement your own type of destination by providing a {@link io.vertx.ext.stomp.DestinationFactory} * returning your own {@link io.vertx.ext.stomp.Destination} object. * * === Acknowledgment * * By default, the STOMP server does nothing when a message is not acknowledged. You can customize this by * providing your own {@link io.vertx.ext.stomp.Destination} implementation. * * The custom destination should call the * * {@link io.vertx.ext.stomp.StompServerHandler#onAck(io.vertx.ext.stomp.StompServerConnection, io.vertx.ext.stomp.Frame, java.util.List)} * and * {@link io.vertx.ext.stomp.StompServerHandler#onNack(io.vertx.ext.stomp.StompServerConnection, io.vertx.ext.stomp.Frame, java.util.List)} * method in order to let the {@link io.vertx.ext.stomp.StompServerHandler} customizes the behavior: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example8} * ---- * * === Customizing the STOMP server * * In addition to the handlers seen above, you can configure almost all aspects of the STOMP server, such as the * actions made when specific frames are received, the `ping` to sent to the client (to implement the heartbeat). * Here are some examples: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example9} * ---- * * Be aware that changing the default behavior may break the compliance with the STOMP specification. So, please look * at the default implementations. * * == STOMP client * * STOMP clients connect to STOMP server and can send and receive frames. * * === Creating a STOMP client * * You create a {@link io.vertx.ext.stomp.StompClient} instance with default options as follows: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example1(io.vertx.core.Vertx)} * ---- * * The previous snippet creates a STOMP client connecting to "0.0.0.0:61613". Once connected, you get a * {@link io.vertx.ext.stomp.StompClientConnection} that let you interact with the server. You can * configure the host and port as follows: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example2(io.vertx.core.Vertx)} * ---- * * To catch connection errors due to authentication issues, or whatever error frames sent by the server during * the connection negotiation, you can register a _error handler_ on the Stomp Client. All * connections created with the client inherit of the error handler (but can have their own): * * [source,$lang] * ---- * {@link examples.StompClientExamples#example21(io.vertx.core.Vertx)} * ---- * * You can also configure the host and port in the {@link io.vertx.ext.stomp.StompClientOptions}: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example3(io.vertx.core.Vertx)} * ---- * * === Closing a STOMP client * * You can close a STOMP client: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example4(io.vertx.core.Vertx)} * ---- * * However, this way would not notify the server of the disconnection. To cleanly close the connection, you should * use the {@link io.vertx.ext.stomp.StompClientConnection#disconnect()} method: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example5(io.vertx.core.Vertx)} * ---- * * If the heartbeat is enabled and if the client did not detect server activity after the configured timeout, the * connection is automatically closed. * * === Handling errors * * On the {@link io.vertx.ext.stomp.StompClientConnection}, you can register an error handler receiving `ERROR` * frames sent by the server. Notice that the server closes the connection with the client after having sent such frame: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example6(io.vertx.core.Vertx)} * ---- * * The client can also be notified when a connection drop has been detected. Connection failures are detected using the * STOMP heartbeat mechanism. When the server has not sent a message in the heartbeat time window, the connection is * closed and the `connectionDroppedHandler` is called (if set). To configure a `connectionDroppedHandler`, call * {@link io.vertx.ext.stomp.StompClientConnection#connectionDroppedHandler(io.vertx.core.Handler)}. The handler can * for instance tries to reconnect to the server: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example14(io.vertx.core.Vertx)} * ---- * * === Configuration * * You can configure various aspect by passing a * {@link io.vertx.ext.stomp.StompClientOptions} when creating the {@link io.vertx.ext.stomp.StompClient}. As the * STOMP client relies on a {@link io.vertx.core.net.NetClient}, you can configure the underlying Net Client from * the {@link io.vertx.ext.stomp.StompClientOptions}. Alternatively, you can pass the {@link io.vertx.core.net.NetClient} * you want to use in the * {@link io.vertx.ext.stomp.StompClient#connect(io.vertx.core.net.NetClient, io.vertx.core.Handler)} method: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example7(io.vertx.core.Vertx, io.vertx.core.net.NetClient)} * ---- * * The {@link io.vertx.ext.stomp.StompClientOptions} let you configure: * * * the host and port ot the STOMP server * * the login and passcode to connect to the server * * whether or not the `content-length` header should be added to the frame if not set explicitly. (enabled by default) * * whether or not the `STOMP` command should be used instead of the `CONNECT` command (disabled by default) * * whether or not the `host` header should be ignored in the `CONNECT` frame (disabled by default) * * the heartbeat configuration (1000, 1000 by default) * * === Subscribing to destinations * * To subscribe to a destination, use: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example8(io.vertx.core.Vertx)} * ---- * * To unsubscribe, use: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example9(io.vertx.core.Vertx)} * ---- * * === Sending messages * * To send a message, use: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example10(io.vertx.core.Vertx)} * ---- * * [language,java,groovy] * ---- * In Java and Groovy, you can use the {@link io.vertx.ext.stomp.utils.Headers} class to ease the header creation. * ---- * * === Acknowledgements * * Clients can send `ACK` and `NACK` frames: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example11(io.vertx.core.Vertx)} * ---- * * === Transactions * * Clients can also create transactions. `ACK`, `NACK` and `SEND` frames sent in the transaction will be delivery * only when the transaction is committed. * * [source,$lang] * ---- * {@link examples.StompClientExamples#example12(io.vertx.core.Vertx)} * ---- * * === Receipt * * Each sent commands can have a _receipt_ handler, notified when the server has processed the message: * * [source,$lang] * ---- * {@link examples.StompClientExamples#example13(io.vertx.core.Vertx)} * ---- * * == Using the STOMP server as a bridge to the vert.x Event Bus * * The STOMP server can be used as a bridge to the vert.x Event Bus. The bridge is bi-directional meaning the STOMP * frames are translated to Event Bus messages and Event Bus messages are translated to STOMP frames. * * To enable the bridge you need to configure the inbound and outbound addresses. Inbound addresses are STOMP * destination that are transferred to the event bus. The STOMP destination is used as the event bus address. Outbound * addresses are event bus addresses that are transferred to STOMP. * * [source,$lang] * ---- * {@link examples.StompServerExamples#example13(io.vertx.core.Vertx)} * ---- * * By default, the bridge use a publish/subscribe delivery (topic). You can configure it to use a point to point * delivery where only one STOMP client or Event Bus consumer is invoked: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example14(io.vertx.core.Vertx)} * ---- * * The permitted options can also be expressed as a "regex" or with a _match_. A _match_ is a structure that the * message payload must meet. For instance, in the next examples, the payload must contains the field "foo" set to * "bar". Structure match only supports JSON object. * * [source,$lang] * ---- * {@link examples.StompServerExamples#example15(io.vertx.core.Vertx)} * ---- * * == Using the STOMP server with web sockets * * If you want to connect a JavaScript client (node.js or a browser) directly with the STOMP server, you can use a * web socket. The STOMP protocol has been adapted to work over web sockets in * http://jmesnil.net/stomp-websocket/doc/[StompJS]. The JavaScript connects directly to the STOMP server and send * STOMP frames on the web socket. It also receives the STOMP frame directly on the web socket. * * To configure the server to use StompJS, you need to: * * 1. Enable the web socket bridge and configure the path of the listening web socket ({@code /stomp} by default). * 2. Import http://jmesnil.net/stomp-websocket/doc/#download[StompJS] in your application (as a script on an * HTML page, or as an npm module (https://www.npmjs.com/package/stompjs). * 3. Connect to the server * * To achieve the first step, you would need a HTTP server, and pass the * {@link io.vertx.ext.stomp.StompServer#webSocketHandler()} result to * {@link io.vertx.core.http.HttpServer#websocketHandler(io.vertx.core.Handler)}: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example16(io.vertx.core.Vertx)} * ---- * * Don't forget to declare the supported sub-protocols. Without this, the connection will be rejected. * * Then follow the instructions from http://jmesnil.net/stomp-websocket/doc/[the StompJS documentation] to connect to * the server. Here is a simple example: * * [source, javascript] * ---- * var url = "ws://localhost:8080/stomp"; * var client = Stomp.client(url); * var callback = function(frame) { * console.log(frame); * }; * * client.connect({}, function() { * var subscription = client.subscribe("foo", callback); * }); * ---- * * == Registering received and writing frame handlers * * STOMP clients, client's connections and server handlers support registering a received * {@link io.vertx.ext.stomp.Frame} handler that would be notified every time a frame is received from the wire. It lets * you log the frames, or implement custom behavior. The handler is already called for {@code PING} * frames, and _illegal / unknown_ frames: * * [source,$lang] * ---- * {@link examples.StompServerExamples#example17(io.vertx.core.Vertx)} * ---- * * The handler is called before the frame is processed, so you can also _modify_ the frame. * * Frames not using a valid STOMP command use the {@code UNKNOWN} command. The original command is written * in the headers using the {@link io.vertx.ext.stomp.Frame#STOMP_FRAME_COMMAND} key. * * You can also register a handler to be notified when a frame is going to be sent (written to the wire): * * [source,$lang] * ---- * {@link examples.StompServerExamples#example18(io.vertx.core.Vertx)} * ---- * */ @ModuleGen(name = "vertx-stomp", groupPackage = "io.vertx") @Document(fileName = "index.adoc") package io.vertx.ext.stomp; import io.vertx.codegen.annotations.ModuleGen; import io.vertx.docgen.Document;