Skip to content

Commit da06b51

Browse files
committed
Adds a new Spring Verticle Factory example
It show how you can implement a VerticleFactory delegating bean initialization to the Spring Container. This is useful if you want to scale across cpus but still let Spring manage your verticles.
1 parent 850ff8e commit da06b51

File tree

8 files changed

+357
-0
lines changed

8 files changed

+357
-0
lines changed

spring-examples/README.adoc

+4
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ The link:spring-example/README.adoc[Vert.x Spring Example] shows how you can acc
1313
== Spring Boot Clustering
1414

1515
The link:spring-boot-clustering/README.adoc[Spring Boot Clustering Examples] show how you can setup a clustered Vert.x embedded in Spring Boot.
16+
17+
== Spring Verticle Factory
18+
19+
The link:spring-verticle-factory/README.adoc[Spring Verticle Factory Example] shows how you can build a Verticle factory based on the Spring container.

spring-examples/pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<module>springboot-example</module>
1515
<module>spring-example</module>
1616
<module>springboot-clustering</module>
17+
<module>spring-verticle-factory</module>
1718
</modules>
1819

1920
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
= Spring Verticle Factory Example
2+
3+
This project shows how you can build a Verticle factory based on the Spring container.
4+
5+
Why not injecting a verticle instance and deploy it directly?
6+
Because Vert.x follows the Multi-Reactor pattern,
7+
so if you want to scale accross your server's CPU,
8+
you need to http://vertx.io/docs/vertx-core/java/#_specifying_number_of_verticle_instances[deploy multiple instances of your verticle].
9+
10+
Only then Vert.x will guarantee that the instances run on separate event loops.
11+
12+
== The code
13+
14+
The `io.vertx.examples.spring.verticlefactory.SpringVerticleFactory` class is an `ApplicationContextAware` Spring component.
15+
It is given an `ApplicationContext` on startup and will use it to create verticle instances.
16+
As it is managed by the Spring container, it must be registered manually (usually one can do this via the Java service loaders).
17+
18+
The `io.vertx.examples.spring.verticlefactory.SpringVerticleFactory` depends on a simple greeter component.
19+
It sets up an HttpServer and use the greeter to generate a hello message for the HTTP response.
20+
21+
IMPORTANT: When you invoke Spring beans from standard verticles, remember that you must not call blocking code
22+
or you will block the event loop.
23+
24+
== Trying
25+
26+
27+
In a terminal, compile and run the example:
28+
29+
[source,shell]
30+
----
31+
mvn compile exec:java@run
32+
----
33+
34+
Then in another tab or window, send a few requests to the server.
35+
36+
[source,shell]
37+
----
38+
seq 10 | while read i; do curl http://localhost:8080/?name=Thomas${i}; echo; done
39+
----
40+
41+
If you look at the console, you'll notice that the requests have indeed been managed by different event loops:
42+
43+
[source]
44+
[subs="verbatim,quotes"]
45+
----
46+
12:46:02.661 [*vert.x-eventloop-thread-2*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas1
47+
12:46:02.685 [*vert.x-eventloop-thread-4*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas2
48+
12:46:02.698 [*vert.x-eventloop-thread-1*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas3
49+
12:46:02.711 [*vert.x-eventloop-thread-3*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas4
50+
12:46:02.722 [*vert.x-eventloop-thread-2*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas5
51+
12:46:02.733 [*vert.x-eventloop-thread-4*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas6
52+
12:46:02.748 [*vert.x-eventloop-thread-1*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas7
53+
12:46:02.761 [*vert.x-eventloop-thread-3*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas8
54+
12:46:02.773 [*vert.x-eventloop-thread-2*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas9
55+
12:46:02.785 [*vert.x-eventloop-thread-4*] INFO io.vertx.examples.spring.verticlefactory.GreetingVerticle - Got request for name: Thomas10
56+
----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2017 Red Hat, Inc.
4+
~
5+
~ Red Hat licenses this file to you under the Apache License, version 2.0
6+
~ (the "License"); you may not use this file except in compliance with the
7+
~ License. You may obtain a copy of the License at:
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
~ License for the specific language governing permissions and limitations
15+
~ under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
23+
<parent>
24+
<groupId>io.vertx</groupId>
25+
<artifactId>spring-examples</artifactId>
26+
<version>3.3.3</version>
27+
</parent>
28+
29+
<artifactId>spring-verticle-factory</artifactId>
30+
31+
<dependencies>
32+
<dependency>
33+
<groupId>io.vertx</groupId>
34+
<artifactId>vertx-core</artifactId>
35+
<version>${project.version}</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.springframework</groupId>
39+
<artifactId>spring-context</artifactId>
40+
<version>4.3.5.RELEASE</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.slf4j</groupId>
44+
<artifactId>jcl-over-slf4j</artifactId>
45+
<version>1.7.20</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>ch.qos.logback</groupId>
49+
<artifactId>logback-classic</artifactId>
50+
<version>1.1.7</version>
51+
</dependency>
52+
</dependencies>
53+
54+
<build>
55+
<plugins>
56+
<plugin>
57+
<artifactId>maven-compiler-plugin</artifactId>
58+
<configuration>
59+
<source>1.8</source>
60+
<target>1.8</target>
61+
</configuration>
62+
</plugin>
63+
<plugin>
64+
<groupId>org.codehaus.mojo</groupId>
65+
<artifactId>exec-maven-plugin</artifactId>
66+
<version>1.4.0</version>
67+
<executions>
68+
<execution>
69+
<id>run</id>
70+
<goals>
71+
<goal>java</goal>
72+
</goals>
73+
<configuration>
74+
<mainClass>io.vertx.examples.spring.verticlefactory.ExampleApplication</mainClass>
75+
</configuration>
76+
</execution>
77+
</executions>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
82+
<profiles>
83+
<profile>
84+
<id>staging</id>
85+
<repositories>
86+
<repository>
87+
<id>staging</id>
88+
<url>https://oss.sonatype.org/content/repositories/iovertx-3295</url>
89+
</repository>
90+
</repositories>
91+
</profile>
92+
</profiles>
93+
94+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2017 Red Hat, Inc.
3+
*
4+
* Red Hat licenses this file to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.vertx.examples.spring.verticlefactory;
18+
19+
import io.vertx.core.DeploymentOptions;
20+
import io.vertx.core.Vertx;
21+
import io.vertx.core.spi.VerticleFactory;
22+
import org.springframework.context.ApplicationContext;
23+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
24+
import org.springframework.context.annotation.ComponentScan;
25+
import org.springframework.context.annotation.Configuration;
26+
27+
/**
28+
* @author Thomas Segismont
29+
*/
30+
@Configuration
31+
@ComponentScan("io.vertx.examples.spring.verticlefactory")
32+
public class ExampleApplication {
33+
34+
public static void main(String[] args) {
35+
Vertx vertx = Vertx.vertx();
36+
37+
ApplicationContext context = new AnnotationConfigApplicationContext(ExampleApplication.class);
38+
39+
VerticleFactory verticleFactory = context.getBean(SpringVerticleFactory.class);
40+
41+
// The verticle factory is registered manually because it is created by the Spring container
42+
vertx.registerVerticleFactory(verticleFactory);
43+
44+
// Scale the verticles on cores: create 4 instances during the deployment
45+
DeploymentOptions options = new DeploymentOptions().setInstances(4);
46+
vertx.deployVerticle(verticleFactory.prefix() + ":" + GreetingVerticle.class.getName(), options);
47+
}
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2017 Red Hat, Inc.
3+
*
4+
* Red Hat licenses this file to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.vertx.examples.spring.verticlefactory;
18+
19+
import org.springframework.stereotype.Component;
20+
21+
/**
22+
* @author Thomas Segismont
23+
*/
24+
@Component
25+
public class Greeter {
26+
27+
public String sayHello(String name) {
28+
return "Hello " + name;
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2017 Red Hat, Inc.
3+
*
4+
* Red Hat licenses this file to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.vertx.examples.spring.verticlefactory;
18+
19+
import io.vertx.core.AbstractVerticle;
20+
import io.vertx.core.Future;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Scope;
25+
import org.springframework.stereotype.Component;
26+
27+
import static org.springframework.beans.factory.config.ConfigurableBeanFactory.*;
28+
29+
/**
30+
* @author Thomas Segismont
31+
*/
32+
@Component
33+
// Prototype scope is needed as multiple instances of this verticle will be deployed
34+
@Scope(SCOPE_PROTOTYPE)
35+
public class GreetingVerticle extends AbstractVerticle {
36+
private static final Logger LOG = LoggerFactory.getLogger(GreetingVerticle.class);
37+
38+
@Autowired
39+
Greeter greeter;
40+
41+
@Override
42+
public void start(Future<Void> startFuture) throws Exception {
43+
vertx.createHttpServer().requestHandler(request -> {
44+
String name = request.getParam("name");
45+
LOG.info("Got request for name: " + name);
46+
if (name == null) {
47+
request.response().setStatusCode(400).end("Missing name");
48+
} else {
49+
// It's fine to call the greeter from the event loop as it's not blocking
50+
request.response().end(greeter.sayHello(name));
51+
}
52+
}).listen(8080, ar -> {
53+
if (ar.succeeded()) {
54+
LOG.info("GreetingVerticle started: @" + this.hashCode());
55+
startFuture.complete();
56+
} else {
57+
startFuture.fail(ar.cause());
58+
}
59+
});
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2017 Red Hat, Inc.
3+
*
4+
* Red Hat licenses this file to you under the Apache License, version 2.0
5+
* (the "License"); you may not use this file except in compliance with the
6+
* License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.vertx.examples.spring.verticlefactory;
18+
19+
import io.vertx.core.Verticle;
20+
import io.vertx.core.spi.VerticleFactory;
21+
import org.springframework.beans.BeansException;
22+
import org.springframework.context.ApplicationContext;
23+
import org.springframework.context.ApplicationContextAware;
24+
import org.springframework.stereotype.Component;
25+
26+
/**
27+
* A {@link VerticleFactory} backed by Spring's {@link ApplicationContext}. It allows to implement verticles as Spring
28+
* beans and thus benefit from dependency injection, ...etc.
29+
*
30+
* @author Thomas Segismont
31+
*/
32+
@Component
33+
public class SpringVerticleFactory implements VerticleFactory, ApplicationContextAware {
34+
35+
private ApplicationContext applicationContext;
36+
37+
@Override
38+
public boolean blockingCreate() {
39+
// Usually verticle instantiation is fast but since our verticles are Spring Beans,
40+
// they might depend on other beans/resources which are slow to build/lookup.
41+
return true;
42+
}
43+
44+
@Override
45+
public String prefix() {
46+
// Just an arbitrary string which must uniquely identify the verticle factory
47+
return "myapp";
48+
}
49+
50+
@Override
51+
public Verticle createVerticle(String verticleName, ClassLoader classLoader) throws Exception {
52+
// Our convention in this example is to give the class name as verticle name
53+
String clazz = VerticleFactory.removePrefix(verticleName);
54+
return (Verticle) applicationContext.getBean(Class.forName(clazz));
55+
}
56+
57+
@Override
58+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
59+
this.applicationContext = applicationContext;
60+
}
61+
}

0 commit comments

Comments
 (0)