How to deploy a verticle on a Web server/Application server? - vert.x

I'm just beginning to learn Vert.x and how to code Verticles. I wonder if it makes any sense to deploy a Verticle from within an Application server or Web server like Tomcat. For example:
public class HelloVerticle extends AbstractVerticle {
private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);
private long counter = 1;
#Override
public void start() {
vertx.setPeriodic(5000, id -> {
logger.info("tick");
});
vertx.createHttpServer()
.requestHandler(req -> {
logger.info("Request #{} from {}", counter++, req.remoteAddress().host());
req.response().end("Hello!");
})
.listen(9080);
logger.info("Open http://localhost:9080/");
}
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new HelloVerticle());
}
}
Obviously the main method needs to be replaced by some ContextListener of any trigger provided by the Application Server. Does it make any sense or it's not supposed to use Vert.x in this Context?
Thanks

Using Vert.x as a Verticle inside a Tomcat app doesn't make much sense from my POV, because it defeats the whole point of componentization.
On the other hand you might want to simply connect to Event Bus to send/publish/receive messages, and is fairly easy to achieve.
I did it for a Grails (SB-based) project and put the Vertx stuff inside a service like:
class VertxService {
Vertx vertx
#PostConstruct
void init() {
def options = [:]
Vertx.clusteredVertx(options){ res ->
if (res.succeeded())
vertx = res.result()
else
System.exit( -1 )
})
}
void publish( addr, msg ){ vertx.publish addr, msg }
//...
}

Related

Vertx - stop method in verticle is not guaranteed

If you run the following code multiple times you will see the inconsistency: some times there are 3 lines displayed, some times there are only 2 lines displayed (the one missing is "Successfully stopped MyVerticle"). Why the .stop method is not called?
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.rxDeployVerticle(new MyVerticle()).subscribe();
Runtime.getRuntime().addShutdownHook(
new Thread(() -> {
//vertx.deploymentIDs().forEach( deploymentId -> vertx.undeploy(deploymentId));
vertx.close(result -> System.out.println("Result" + result));
System.out.println("Successfully stopped Vertx");
})
);
}
}
class MyVerticle extends AbstractVerticle {
#Override
public void start(Future<Void> startFuture) {
System.out.println("Successfully started MyVerticle");
startFuture.complete();
}
#Override
public void stop(Future<Void> stopFuture) {
System.out.println("Successfully stopped MyVerticle");
stopFuture.complete();
}
}
The method stop() is invoked when Vert.x undeploys a verticle.
When terminating your application, Vert.x will attempt to undeploy the verticles as well, but it's a race between event loop still running and your application shutting down.

Vertx event bus not communicating in single JVM

I have two verticles as below
First verticle is just listening on a address test and reply to messages
public class FirstVerticle extends AbstractVerticle {
#Override
public void start() {
Logger logger = LogManager.getLogger(FirstVerticle.class);
vertx.eventBus().consumer("test",message->{
logger.info("message received " + message.headers());
message.reply("hi!!!!");
});
}
}
Second verticle just sends a message to address test
public class SecondVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
Logger logger = LogManager.getLogger(SecondVerticle.class);
vertx.eventBus().request("test","hey there",handler->{
if(handler.failed())
logger.error("Failed to get data"+handler.cause());
else
logger.info("response " + handler.result().headers());
});
}
}
The two vertciles are deployed using a common main class
Vertx.clusteredVertx(new VertxOptions().setHAEnabled(true), vertx ->
vertx.result().deployVerticle(verticleName, new DeploymentOptions().setHa(true))
);
When running as a separate program and deploy the verticles in different JVM, verticles can communicate with each other using event bus, but when deploying two verticles using common class at a time is not working, getting below error
Failed to get data(TIMEOUT,-1) Timed out after waiting 30000(ms) for a reply. address:
__vertx.reply.9da86cc6-f689-47d5-a5b4-bceafbce254a, repliedAddress: test
Any help is highly appreciated.
Thanks in advance.
Vertx event bus can communicate within a JVM.
It was my mistake that I configured the event bus interceptor, but did not completed the chain by calling context.next().

Purpose and behaviour of init() in Vertx class

I have the following verticle for testing purposes:
public class UserVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(UserVerticle.class);
#Override
public void start(Future<Void> sf) {
log.info("start()");
JsonObject cnf = config();
log.info("start.config={}", cnf.toString());
sf.complete();
}
#Override
public void stop(Future<Void> sf) {
log.info("stop()");
sf.complete();
}
private void onMessage(Message<JsonObject> message) { ... }
log.info("onMessage(message={})", message);
}
}
Is is deployed from the main verticle with
vertx.deployVerticle("org.buguigny.cluster.UserVerticle",
new DeploymentOptions()
.setInstances(1)
.setConfig(new JsonObject()
.put(some_key, some_data)
),
ar -> {
if(ar.succeeded()) {
log.info("UserVerticle(uname={}, addr={}) deployed", uname, addr);
// continue when OK
}
else {
log.error("Could not deploy UserVerticle(uname={}). Cause: {}", uname, ar.cause());
// continue when KO
}
});
This code works fine.
I had a look at the Verticle documentation and discovered an init() callback method I didn't see before. As the documentation doesn't say much about what it really does, I defined it to see where in the life cycle of a verticle it gets called.
#Override
public void init(Vertx vertx, Context context) {
log.info("init()");
JsonObject cnf = context.config();
log.info("init.config={}", cnf.toString());
}
However, when init() is defined I get a java.lang.NullPointerException on the line where I call JsonObject cnf = config(); in start():
java.lang.NullPointerException: null
at io.vertx.core.AbstractVerticle.config(AbstractVerticle.java:85)
at org.buguigny.cluster.UserVerticle.start(UserVerticle.java:30)
at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$8(DeploymentManager.java:494)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
at io.vertx.core.impl.EventLoopContext.lambda$executeAsync$0(EventLoopContext.java:38)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
My questions are:
Q1 : any clue why NullPointerException is thrown?
Q2 : what is the purpose of init()? Is it internal to Vertx or can it be be implemented by client code to, for example, define some fields in the verticle objects passed in deployment config ?
The init method is for internal usage and documented as such in the Javadoc. Here's the source code:
/**
* Initialise the verticle.<p>
* This is called by Vert.x when the verticle instance is deployed. Don't call it yourself.
* #param vertx the deploying Vert.x instance
* #param context the context of the verticle
*/
#Override
public void init(Vertx vertx, Context context) {
this.vertx = vertx;
this.context = context;
}
If init is documented in any user documentation it's a mistake, please report it.

Disconnect client from IHubContext<THub>

I can call InvokeAsync from server code using the IHubContext interface, but sometimes I want to force these clients to disconnect.
So, is there any way to disconnect clients from server code that references the IHubContext interface?
Step 1:
using Microsoft.AspNetCore.Connections.Features;
using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR;
public class ErrorService
{
readonly HashSet<string> PendingConnections = new HashSet<string>();
readonly object PendingConnectionsLock = new object();
public void KickClient(string ConnectionId)
{
//TODO: log
if (!PendingConnections.Contains(ConnectionId))
{
lock (PendingConnectionsLock)
{
PendingConnections.Add(ConnectionId);
}
}
}
public void InitConnectionMonitoring(HubCallerContext Context)
{
var feature = Context.Features.Get<IConnectionHeartbeatFeature>();
feature.OnHeartbeat(state =>
{
if (PendingConnections.Contains(Context.ConnectionId))
{
Context.Abort();
lock (PendingConnectionsLock)
{
PendingConnections.Remove(Context.ConnectionId);
}
}
}, Context.ConnectionId);
}
}
Step 2:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<ErrorService>();
...
}
Step 3:
[Authorize(Policy = "Client")]
public class ClientHub : Hub
{
ErrorService errorService;
public ClientHub(ErrorService errorService)
{
this.errorService = errorService;
}
public async override Task OnConnectedAsync()
{
errorService.InitConnectionMonitoring(Context);
await base.OnConnectedAsync();
}
....
Disconnecting without Abort() method:
public class TestService
{
public TestService(..., ErrorService errorService)
{
string ConnectionId = ...;
errorService.KickClient(ConnectionId);
In alpha 2 there is the Abort() on HubConnectionContext you could use to terminate a connection. I don't see, however, an easy way to access it from outside the hub.
Because you control the clients you could just invoke a client method and tell the client to disconnect. The advantage is that the client disconnect gracefully. The disadvantage is that it requires sending the message to the client instead of disconnecting the client solely on the server side.

Best way to handle incoming messages with XMPP

Is there a work-around to get Spring to handle incoming messages from XMPP? I have tried many different configurations to get an inbound-channel-adapter to respond to incoming XMPP messages and nothing happens. I know that they show up at the Spring Integration layer (I can see that in the logs) but they are ignored. Is there any way to get them into my application layer? I hope to avoid needing to make changes to Spring Integration itself if I can.
Here is my integration configuration:
<int-xmpp:inbound-channel-adapter id="gcmIn"
channel="gcmInChannel"
xmpp-connection="gcmConnection"
auto-startup="true"
/>
<bean id="inboundBean" class="example.integration.GcmInputHandler"/>
<int:service-activator input-channel="gcmInChannel" output-channel="nullChannel" ref="inboundBean" method="handle"/>
Using the outbound-channel-adapter works fine. I can send messages over GCM 100% easily. But inbound does nothing, even though I know the messages are coming in.
Thanks
Not a very clean one, you would need to overwrite the ChatMessageListeningEndpoint, which drops all empty body messages.
This one needs then to be used as inbound-channel adapter in your config.
In addition you need to register the GCM package extension on the Smack Provider Manager, otherwise you lose the JSON message.
Working on a sample project -- so if you need more help let me know and I will post a link as soon it works somehow in a understandable way.
Here a sample GCM Input Adapter
public class GcmMessageListeningEndpoint extends ChatMessageListeningEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(GcmMessageListeningEndpoint.class);
#Setter
protected PacketListener packetListener = new GcmPacketListener();
protected XmppHeaderMapper headerMapper = new DefaultXmppHeaderMapper();
public GcmMessageListeningEndpoint(XMPPConnection connection) {
super(connection);
ProviderManager.addExtensionProvider(GcmPacketExtension.GCM_ELEMENT_NAME, GcmPacketExtension.GCM_NAMESPACE,
new PacketExtensionProvider() {
#Override
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
String json = parser.nextText();
return new GcmPacketExtension(json);
}
});
}
#Override
public void setHeaderMapper(XmppHeaderMapper headerMapper) {
super.setHeaderMapper(headerMapper);
this.headerMapper = headerMapper;
if (this.headerMapper == null) throw new IllegalArgumentException("Null XmppHeaderMapper isn't supported!");
}
public String getComponentType() {
return "xmpp:inbound-channel-adapter-gcm";
}
#Override
protected void doStart() {
Assert.isTrue(this.initialized, this.getComponentName() + " [" + this.getComponentType() + "] must be initialized");
this.xmppConnection.addPacketListener(this.packetListener, null);
}
#Override
protected void doStop() {
if (this.xmppConnection != null) {
this.xmppConnection.removePacketListener(this.packetListener);
}
}
class GcmPacketListener implements PacketListener {
#Override
public void processPacket(Packet packet) throws NotConnectedException {
if (packet instanceof org.jivesoftware.smack.packet.Message) {
org.jivesoftware.smack.packet.Message xmppMessage = (org.jivesoftware.smack.packet.Message) packet;
Map<String, ?> mappedHeaders = headerMapper.toHeadersFromRequest(xmppMessage);
sendMessage(MessageBuilder.withPayload(xmppMessage).copyHeaders(mappedHeaders).build());
} else {
LOG.warn("Unsuported Packet {}", packet);
}
}
}
}
And here the new configuration for the inbound-channel-adapter remove the one in XML:
#Bean
public GcmMessageListeningEndpoint inboundAdpater(XMPPConnection connection, MessageChannel gcmInChannel) {
GcmMessageListeningEndpoint endpoint = new GcmMessageListeningEndpoint(connection);
endpoint.setOutputChannel(gcmInChannel);
return endpoint;
}