Chapter 8. Changes in client components This section explains the changes in Eclipse Vert.x clients. 8.1. Changes in Eclipse Vert.x Kafka client The following section describes the changes in Eclipse Vert.x Kafka client. 8.1.1. AdminUtils Class is no longer available The AdminUtils class is no longer available. Use the new KafkaAdminClient class instead to perform administrative operations on a Kafka cluster. 8.1.2. Flush methods use asynchronous handler The flush methods in KafkaProducer class use Handler<AsyncResult<Void>> instead of Handler<Void> . 8.2. Changes in Eclipse Vert.x JDBC client From Eclipse Vert.x 4, the JDBC client supports SQL client. The SQL common module has also been merged in JDBC client, that is, io.vertx:vertx-sql-common merged in io.vertx:vertx-jdbc-client module. You will have to remove the io.vertx:vertx-sql-common dependency file because io.vertx:vertx-jdbc-client will include it. With the merging of SQL common client, all the database APIs have been consolidated into the JDBC client. In Eclipse Vert.x 4, the SQL client has been updated to include the following clients: Reactive PostgreSQL client. In earlier releases, it included a reactive PostgreSQL client. Reactive MySQL client Reactive DB2 client Continues to include reactive PostgreSQL client. This client was available in Eclipse Vert.x 3.x releases as well. Existing JDBC client now includes both the JDBC client API and the SQL client API The reactive implementations use the database network protocols. This makes them resource-efficient. JDBC calls to database are blocking calls. The JDBC client uses worker threads to make these calls non-blocking. The following section describes the changes in Eclipse Vert.x JDBC client. 8.2.1. Creating a pool In Eclipse Vert.x 4, you can create a pool using the JDBC client APIs. In earlier releases, you could create only clients. You could not create pools. The following example shows how to create a client in Eclipse Vert.x 3.x. // 3.x SQLClient client = JDBCClient.create(vertx, jsonConfig); The following example shows how to create a pool in Eclipse Vert.x 4. // 4.x JDBCPool pool = JDBCPool.pool(vertx, jsonConfig); Note Though the Eclipse Vert.x 3.x APIs are supported in Eclipse Vert.x 4, it is recommended that you use the new JDBC client APIs in your applications. A pool enables you to perform simple queries. You do not need to manage connections for simple queries. However, for complex queries or multiple queries, you must manage your connections. The following example shows how to manage connections for queries in Eclipse Vert.x 3.x. // 3.x client.getConnection(res -> { if (res.succeeded()) { SQLConnection connection = res.result(); // Important, do not forget to return the connection connection.close(); } else { // Failed to get connection } }); The following example shows how to manage connections for queries in Eclipse Vert.x 4. // 4.x pool .getConnection() .onFailure(e -> { // Failed to get a connection }) .onSuccess(conn -> { // Important, do not forget to return the connection conn.close(); }); 8.2.2. Support for Typsesafe Config You can use jsonConfig for configurations. However, using the jsonConfig may sometimes result in errors. To avoid these errors, the JDBC client introduces Typesafe Config. The following example shows the basic structure of a Typesafe Config. // 4.x ONLY!!! JDBCPool pool = JDBCPool.pool( vertx, // configure the connection new JDBCConnectOptions() // H2 connection string .setJdbcUrl("jdbc:h2:~/test") // username .setUser("sa") // password .setPassword(""), // configure the pool new PoolOptions() .setMaxSize(16) ); Note To use Typesafe Config, you must include the agroal connection pool in your project. The pool does not expose many configuration options and makes the configuration easy to use. 8.2.3. Running SQL queries This section shows you how to run queries in the JDBC client. 8.2.3.1. Running one shot queries The following example shows how to run queries without managing the connection in Eclipse Vert.x 3.x. // 3.x client.query("SELECT * FROM user WHERE emp_id > ?", new JsonArray().add(1000), res -> { if (res.succeeded()) { ResultSet rs = res2.result(); // You can use these results in your application } }); The following example shows how to run queries without managing the connection in Eclipse Vert.x 4. // 4.x pool .preparedQuery("SELECT * FROM user WHERE emp_id > ?") // the emp id to look up .execute(Tuple.of(1000)) .onSuccess(rows -> { for (Row row : rows) { System.out.println(row.getString("FIRST_NAME")); } }); 8.2.3.2. Running queries on managed connections The following example shows how to run queries on managed connections in Eclipse Vert.x 4. pool .getConnection() .onFailure(e -> { // Failed to get a connection }) .onSuccess(conn -> { conn .query("SELECT * FROM user") .execute() .onFailure(e -> { // Handle the failure // Important, do not forget to return the connection conn.close(); }) .onSuccess(rows -> { for (Row row : rows) { System.out.println(row.getString("FIRST_NAME")); } // Important, do not forget to return the connection conn.close(); }); }); 8.2.4. Support for stored procedures Stored procedures are supported in the JDBC client. The following example shows how to pass IN arguments in Eclipse Vert.x 3.x. // 3.x connection.callWithParams( "{ call new_customer(?, ?) }", new JsonArray().add("John").add("Doe"), null, res -> { if (res.succeeded()) { // Success! } else { // Failed! } }); The following example shows how to pass IN arguments in Eclipse Vert.x 4. // 4.x client .preparedQuery("{call new_customer(?, ?)}") .execute(Tuple.of("Paulo", "Lopes")) .onSuccess(rows -> { ... }); In Eclipse Vert.x 3.x, the support for combining the IN and OUT arguments was very limited due to the available types. In Eclipse Vert.x 4, the pool is type safe and can handle the combination of IN and OUT arguments. You can also use INOUT parameters in your applications. The following example shows handling of arguments in Eclipse Vert.x 3.x. // 3.x connection.callWithParams( "{ call customer_lastname(?, ?) }", new JsonArray().add("John"), new JsonArray().addNull().add("VARCHAR"), res -> { if (res.succeeded()) { ResultSet result = res.result(); } else { // Failed! } }); The following example shows handling of arguments in Eclipse Vert.x 4. // 4.x client .preparedQuery("{call customer_lastname(?, ?)}") .execute(Tuple.of("John", SqlOutParam.OUT(JDBCType.VARCHAR))) .onSuccess(rows -> { ... }); In the JDBC client, the data types have been updated. For an argument of type OUT , you can specify its return type. In the example, the OUT argument is specified as type VARCHAR which is a JDBC constant. The types are not bound by JSON limitations. You can now use database specific types instead of text constants for the type name. 8.3. Changes in Eclipse Vert.x mail client The following section describes the changes in Eclipse Vert.x mail client. 8.3.1. MailAttachment is available as an interface From Eclipse Vert.x 4 onwards, MailAttachment is available as an interface. It enables you to use the mail attachment functionality in a stream. In earlier releases of Eclipse Vert.x, MailAttachment was available as a class and attachment for mails was represented as a data object. 8.3.2. Mail configuration interface extends the net client options MailConfig interface extends the NetClientOptions interface. Due to this extension, mail configuration also supports the proxy setting of the NetClient . 8.4. Changes in Eclipse Vert.x AMQP client The following section describes the changes in Eclipse Vert.x AMQP client. 8.4.1. Removed methods in AMQP client that contain AmqpMessage argument The AMQP client methods that had Handler<AmqpMessage> as an argument have been removed. In earlier releases, you could set this handler on ReadStream<AmqpMessage> . However, if you migrate your applications to use futures, such methods cannot be used. Removed methods Replacing methods AmqpClient.createReceiver(String address, Handler<AmqpMessage> messageHandler, ... ) AmqpClient createReceiver(String address, Handler<AsyncResult<AmqpReceiver>> completionHandler) AmqpConnection createReceiver(... , Handler<AsyncResult<AmqpReceiver>> completionHandler) AmqpConnection createReceiver(String address, Handler<AsyncResult<AmqpReceiver>> completionHandler) AmqpConnection createReceiver(.., Handler<AmqpMessage> messageHandler, Handler<AsyncResult<AmqpReceiver>> completionHandler) AmqpConnection createReceiver(String address, Handler<AsyncResult<AmqpReceiver>> completionHandler) 8.5. Changes in Eclipse Vert.x MongoDB client The following section describes the changes in Eclipse Vert.x MongoDB client. 8.5.1. Methods removed from MongoDB client The following methods have been removed from MongoClient class. Removed methods Replacing methods MongoClient.update() MongoClient.updateCollection() MongoClient.updateWithOptions() MongoClient.updateCollectionWithOptions() MongoClient.replace() MongoClient.replaceDocuments() MongoClient.replaceWithOptions() MongoClient.replaceDocumentsWithOptions() MongoClient.remove() MongoClient.removeDocuments() MongoClient.removeWithOptions() MongoClient.removeDocumentsWithOptions() MongoClient.removeOne() MongoClient.removeDocument() MongoClient.removeOneWithOptions() MongoClient.removeDocumentsWithOptions() 8.6. Changes in EventBus JavaScript client In Eclipse Vert.x 4, the EventBus JavaScript client module is available in a new location. You will have to update your build systems to use the module from the new location. In Eclipse Vert.x 3.x, the event bus JavaScript client was available in various locations, for example: Maven Central NPM Bower.io CDNJS webjars In Eclipse Vert.x 4, the JavaScript client is available only in npm . The EventBus JavaScript client module can be accessed from the following locations: CDN npm packages Use the following code in your build scripts to access the module. JSON scripts XML scripts <dependency> <groupId>org.webjars.npm</groupId> <artifactId>vertx__eventbus-bridge-client.js</artifactId> <version>1.0.0-1</version> </dependency> 8.6.1. Versioning of JavaScript client Before Eclipse Vert.x 4, every Eclipse Vert.x release included a new release of the JavaScript client. However, from Eclipse Vert.x 4 onward, a new version of JavaScript client will be available in npm only if there changes in the client. You do not need to update your client application for every Eclipse Vert.x release, unless there is a version change. 8.7. Changes in Eclipse Vert.x Redis client In Eclipse Vert.x 4, use the Redis class to work with Redis client. The class RedisClient is no longer available. NOTE To help you migrate your applications from RedisClient to Redis class, a helper class RedisAPI is available. RedisAPI enables you to replicate the functionality similar to RedisClient class. The new class contains all the enhancements in protocols and Redis server features. Use the new class to: Work with all Redis commands Connect to single servers Connect to high availability servers where Redis Sentinel is enabled Connect to cluster configurations of Redis Execute requests in Redis extensions Communicate with both RESP2 and RESP3 server protocol servers 8.7.1. Migrating existing Redis client applications to new client You can migrate your existing applications to new Redis client directly or use the helper class RedisAPI to migrate your applications in two steps. Before migrating the applications you must create the client. 8.7.1.1. Creating the client The following example shows how a Redis client was created in Eclipse Vert.x 3.x releases. // Create the redis client (3.x) RedisClient client = RedisClient .create(vertx, new RedisOptions().setHost(host)); The following example shows how to create a Redis client in Eclipse Vert.x 4. // Create the redis client (4.x) Redis client = Redis .createClient( vertx, "redis://server.address:port"); In Eclipse Vert.x 4, the client uses the following standard connection string syntax: redis[s]://[[user]:password@]server[:port]/[database] 8.7.1.2. Migrating applications to RedisAPI Using the 'RedisAPI` you can now decide how to manage the connection: You can let the client manage the connection for you using a pool. Or You can control the connection by requesting a new connection. You must ensure to close or return the connection when done. You must create the client and then update the applications to handle requests. The following example shows how to handle requests after creating the client in Eclipse Vert.x 3.x releases. // Using 3.x // omitting the error handling for brevity client.set("key", "value", s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } }); The following example shows how to handle requests after creating the client in Eclipse Vert.x 4. The example uses a list for setting the key-value pairs instead of hard coding options. See Redis SET command for more information on arguments available for the command. // Using 4.x // omitting the error handling for brevity // 1. Wrap the client into a RedisAPI api = RedisAPI.api(client); // 2. Use the typed API api.set( Arrays.asList("key", "value"), s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } }); 8.7.1.3. Migrating applications directly to Redis client When you migrate to the new Redis client directly: You can use all the new Redis commands. You can use extensions. You may reduce a few conversions from helper class to new client, which might improve the performance of your application. You must create the client and then update the applications to handle requests. The following example shows how to set and get requests after creating the client in Eclipse Vert.x 3.x releases. // Using 3.x // omitting the error handling for brevity client.set("key", "value", s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } }); The following example shows how to handle requests after creating the client in Eclipse Vert.x 4. // Using 4.x // omitting the error handling for brevity import static io.vertx.redis.client.Request.cmd; import static io.vertx.redis.client.Command.*; client.send(cmd(SET).arg("key").arg("value"), s -> { if (s.succeeded()) { System.out.println("key stored"); client.send(cmd(GET).arg("key"), g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } }); In Eclipse Vert.x 4, all the interactions use the send(Request) method. 8.7.1.4. Migrating responses In Eclipse Vert.x 3.x, the client used to hardcode all known commands till Redis 5, and the responses were also typed according to the command. In the new client, the commands are not hardcoded. The responses are of the type Response . The new wire protocol has more range of types. In older client, a response would be of following types: null Long String JsonArray JsonObject (For INFO and HMGET array responses) In the new client, the response is of following types: null Response The Response object has type converters. For example, converters such as: toString() toInteger() toBoolean() toBuffer() If the received data is not of the requested type, then the type converters convert it to the closet possible data type. When the conversion to a particular type is not possible, the UnsupportedOperationException is thrown. For example, conversion from String to List or Map is not possible. You can also handle collections, because the Response object implements the Iterable interface. The following example shows how to perform a MGET request. // Using 4.x // omitting the error handling for brevity import static io.vertx.redis.client.Request.cmd; import static io.vertx.redis.client.Command.*; client.send(cmd(MGET).arg("key1").arg("key2").arg("key3"), mget -> { mget.result() .forEach(value -> { // Use the single value 8.7.2. Updates in Eclipse Vert.x Redis client This section describes changes in Redis client. 8.7.2.1. Removed deprecated term "slave" from Redis roles and node options The deprecated term "slave" has been replaced with "replica" in Redis roles and node options. Roles The following example shows the usage of SLAVE role in Eclipse Vert.x 3.x releases. The following example shows the usage of REPLICA role in Eclipse Vert.x 4. Node options The following example shows you usage of node type RedisSlaves in Eclipse Vert.x 3.x releases. The following example shows you usage of node type RedisReplicas in Eclipse Vert.x 4.

Commands:
// 3.x SQLClient client = JDBCClient.create(vertx, jsonConfig);
// 4.x JDBCPool pool = JDBCPool.pool(vertx, jsonConfig);
// 3.x client.getConnection(res -> { if (res.succeeded()) { SQLConnection connection = res.result(); // Important, do not forget to return the connection connection.close(); } else { // Failed to get connection } });
// 4.x pool .getConnection() .onFailure(e -> { // Failed to get a connection }) .onSuccess(conn -> { // Important, do not forget to return the connection conn.close(); });
// 4.x ONLY!!! JDBCPool pool = JDBCPool.pool( vertx, // configure the connection new JDBCConnectOptions() // H2 connection string .setJdbcUrl("jdbc:h2:~/test") // username .setUser("sa") // password .setPassword(""), // configure the pool new PoolOptions() .setMaxSize(16) );
// 3.x client.query("SELECT * FROM user WHERE emp_id > ?", new JsonArray().add(1000), res -> { if (res.succeeded()) { ResultSet rs = res2.result(); // You can use these results in your application } });
// 4.x pool .preparedQuery("SELECT * FROM user WHERE emp_id > ?") // the emp id to look up .execute(Tuple.of(1000)) .onSuccess(rows -> { for (Row row : rows) { System.out.println(row.getString("FIRST_NAME")); } });
pool .getConnection() .onFailure(e -> { // Failed to get a connection }) .onSuccess(conn -> { conn .query("SELECT * FROM user") .execute() .onFailure(e -> { // Handle the failure // Important, do not forget to return the connection conn.close(); }) .onSuccess(rows -> { for (Row row : rows) { System.out.println(row.getString("FIRST_NAME")); } // Important, do not forget to return the connection conn.close(); }); });
// 3.x connection.callWithParams( "{ call new_customer(?, ?) }", new JsonArray().add("John").add("Doe"), null, res -> { if (res.succeeded()) { // Success! } else { // Failed! } });
// 4.x client .preparedQuery("{call new_customer(?, ?)}") .execute(Tuple.of("Paulo", "Lopes")) .onSuccess(rows -> { });
// 3.x connection.callWithParams( "{ call customer_lastname(?, ?) }", new JsonArray().add("John"), new JsonArray().addNull().add("VARCHAR"), res -> { if (res.succeeded()) { ResultSet result = res.result(); } else { // Failed! } });
// 4.x client .preparedQuery("{call customer_lastname(?, ?)}") .execute(Tuple.of("John", SqlOutParam.OUT(JDBCType.VARCHAR))) .onSuccess(rows -> { });
{ "devDependencies": { "@vertx/eventbus-bridge-client.js": "1.0.0-1" } }
<dependency> <groupId>org.webjars.npm</groupId> <artifactId>vertx__eventbus-bridge-client.js</artifactId> <version>1.0.0-1</version> </dependency>
// Create the redis client (3.x) RedisClient client = RedisClient .create(vertx, new RedisOptions().setHost(host));
// Create the redis client (4.x) Redis client = Redis .createClient( vertx, "redis://server.address:port");
redis[s]://[[user]:password@]server[:port]/[database]
// Using 3.x // omitting the error handling for brevity client.set("key", "value", s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } });
// Using 4.x // omitting the error handling for brevity // 1. Wrap the client into a RedisAPI api = RedisAPI.api(client); // 2. Use the typed API api.set( Arrays.asList("key", "value"), s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } });
// Using 3.x // omitting the error handling for brevity client.set("key", "value", s -> { if (s.succeeded()) { System.out.println("key stored"); client.get("key", g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } });
// Using 4.x // omitting the error handling for brevity import static io.vertx.redis.client.Request.cmd; import static io.vertx.redis.client.Command.*; client.send(cmd(SET).arg("key").arg("value"), s -> { if (s.succeeded()) { System.out.println("key stored"); client.send(cmd(GET).arg("key"), g -> { if (s.succeeded()) { System.out.println("Retrieved value: " + s.result()); } }); } });
// Using 4.x // omitting the error handling for brevity import static io.vertx.redis.client.Request.cmd; import static io.vertx.redis.client.Command.*; client.send(cmd(MGET).arg("key1").arg("key2").arg("key3"), mget -> { mget.result() .forEach(value -> { // Use the single value
// Before (3.x) Redis.createClient( rule.vertx(), new RedisOptions() .setType(RedisClientType.SENTINEL) .addConnectionString("redis://localhost:5000") .setMasterName("sentinel7000") .setRole(RedisRole.SLAVE));
// After (4.x) Redis.createClient( rule.vertx(), new RedisOptions() .setType(RedisClientType.SENTINEL) .addConnectionString("redis://localhost:5000") .setMasterName("sentinel7000") .setRole(RedisRole.REPLICA));
// Before (3.9) options.setUseSlaves(RedisSlaves);
// After (4.x) options.setUseReplicas(RedisReplicas);