Skip to content

Latest commit

 

History

History
161 lines (113 loc) · 7.51 KB

README.adoc

File metadata and controls

161 lines (113 loc) · 7.51 KB

Refactoring to Vert.x services

Tip
The corresponding source code is in the step-3 folder of the guide repository.

The previous refactoring was a big step forward compared to our initial implementation, as we extracted independent and configurable verticles connected using asynchronous messages on the event bus. We also saw that we could deploy several instances of a given verticle to better cope with load and to better leverage CPU cores.

In this section we see how to design and use Vert.x services. The main advantage of a service is that it defines an interface for doing certain operations that a verticle exposes. We also leverage code generation for all the event bus messaging plumbing, instead of crafting it ourselves like we did in the previous section.

We are also going to refactor the code into different Java packages:

step-3/src/main/java/
└── io
    └── vertx
        └── guides
            └── wiki
                ├── MainVerticle.java
                ├── database
                │   ├── ErrorCodes.java
                │   ├── SqlQuery.java
                │   ├── WikiDatabaseService.java
                │   ├── WikiDatabaseServiceImpl.java
                │   ├── WikiDatabaseVerticle.java
                │   └── package-info.java
                └── http
                    └── HttpServerVerticle.java

io.vertx.guides.wiki will now contain the main verticle, io.vertx.guides.wiki.database the database verticle and service, and io.vertx.guides.wiki.http the HTTP server verticle.

Maven configuration changes

First, we need to add the following 2 dependencies to our project. Obviously we need the vertx-service-proxy APIs:

link:pom.xml[role=include]

We need the Vert.x code generation module as a compilation-time only dependency (hence the provided scope):

link:pom.xml[role=include]

Next we have to tweak the maven-compiler-plugin configuration to use code generation, which is done via a javac annotation processor:

link:pom.xml[role=include]

Note that the generated code is put in src/main/generated, which some integrated development environments like IntelliJ IDEA will automatically pick up on the classpath.

It is also a good idea to update the maven-clean-plugin to remove those generated files:

link:pom.xml[role=include]
Tip
The full documentation on Vert.x services is available at http://vertx.io/docs/vertx-service-proxy/java/

Database service interface

Defining a service interface is as simple as defining a Java interface, except that there are certain rules to respect for code generation to work and also to ensure inter-operability with other code in Vert.x.

The beginning of the interface definition is:

link:src/main/java/io/vertx/guides/wiki/database/WikiDatabaseService.java[role=include]
  1. The ProxyGen annotation is used to trigger the code generation of a proxy for clients of that service.

  2. The Fluent annotation is optional, but allows fluent interfaces where operations can be chained by returning the service instance. This is mostly useful for the code generator when the service shall be consumed from other JVM languages.

  3. Parameter types need to be strings, Java primitive types, JSON objects or arrays, any enumeration type or a java.util collection (List / Set / Map) of the previous types. The only way to support arbitrary Java classes is to have them as Vert.x data objects, annotated with @DataObject. The last opportunity to pass other types is service reference types.

  4. Since services provide asynchronous results, the last argument of a service method needs to be a Handler<AsyncResult<T>> where T is any of the types suitable for code generation as described above.

It is a good practice that service interfaces provide static methods to create instances of both the actual service implementation and proxy for client code over the event bus.

We define create as simply delegating to the implementation class and its constructor:

link:src/main/java/io/vertx/guides/wiki/database/WikiDatabaseService.java[role=include]

The Vert.x code generator creates the proxy class and names it by suffixing with VertxEBProxy. Constructors of these proxy classes need a reference to the Vert.x context as well as a destination address on the event bus:

link:src/main/java/io/vertx/guides/wiki/database/WikiDatabaseService.java[role=include]
Note
The SqlQuery and ErrorCodes enumeration types that were inner classes in the previous iteration have been extracted to package-protected types, see SqlQuery.java and ErrorCodes.java.

Database service implementation

The service implementation is a straightforward port of the previous WikiDatabaseVerticle class code. The essential difference is the support of the asynchronous result handler in the constructor (to report the initialization outcome) and in the service methods (to report the operation success).

The class code is the following:

link:src/main/java/io/vertx/guides/wiki/database/WikiDatabaseServiceImpl.java[role=include]

There is one last step required before the proxy code generation works: the service package needs to have a package-info.java annotated to define a Vert.x module:

link:src/main/java/io/vertx/guides/wiki/database/package-info.java[role=include]

Exposing the database service from the database verticle

As most of the database handling code has been moved to WikiDatabaseServiceImpl, the WikiDatabaseVerticle class now consists of 2 methods: the start method to register the service and a utility method to load SQL queries:

link:src/main/java/io/vertx/guides/wiki/database/WikiDatabaseVerticle.java[role=include]
  1. We register the service here.

Registering a service requires an interface class, a Vert.x context, an implementation and an event bus destination.

The WikiDatabaseServiceVertxEBProxy generated class handles receiving messages on the event bus and then dispatching them to the WikiDatabaseServiceImpl. What it does is actually very close to what we did in the previous section: messages are being sent with a action header to specify which method to invoke, and parameters are encoded in JSON.

Obtaining a database service proxy

The final steps to refactoring to Vert.x services is to adapt the HTTP server verticle to obtain a proxy to the database service and use it in the handlers instead of the event bus.

First, we need to create the proxy when the verticle starts:

link:src/main/java/io/vertx/guides/wiki/http/HttpServerVerticle.java[role=include]
  1. We just need to make sure to use the same event bus destination as the service that was published by WikiDatabaseVerticle.

Then, we need to replace calls to the event bus with calls to the database service:

link:src/main/java/io/vertx/guides/wiki/http/HttpServerVerticle.java[role=include]

The WikiDatabaseServiceVertxProxyHandler generated class deals with forwarding calls as event bus messages.

Tip
It is still perfectly possible to consume a Vert.x service directly via event bus messages since this is what generated proxys do.