Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.vertx.core.eventbus.impl.EventBusImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2014 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.
*/
package io.vertx.core.eventbus.impl;
import io.vertx.core.*;
import io.vertx.core.eventbus.*;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.spi.metrics.EventBusMetrics;
import io.vertx.core.spi.metrics.MetricsProvider;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
/**
* A local event bus implementation
*
* @author Tim Fox T
*/
public class EventBusImpl implements EventBus, MetricsProvider {
private static final Logger log = LoggerFactory.getLogger(EventBusImpl.class);
private final List> interceptors = new CopyOnWriteArrayList<>();
private final AtomicLong replySequence = new AtomicLong(0);
protected final VertxInternal vertx;
protected final EventBusMetrics metrics;
protected final ConcurrentMap handlerMap = new ConcurrentHashMap<>();
protected final CodecManager codecManager = new CodecManager();
protected volatile boolean started;
public EventBusImpl(VertxInternal vertx) {
this.vertx = vertx;
this.metrics = vertx.metricsSPI().createMetrics(this);
}
@Override
public EventBus addInterceptor(Handler interceptor) {
interceptors.add(interceptor);
return this;
}
@Override
public EventBus removeInterceptor(Handler interceptor) {
interceptors.remove(interceptor);
return this;
}
public synchronized void start(Handler> completionHandler) {
if (started) {
throw new IllegalStateException("Already started");
}
started = true;
completionHandler.handle(Future.succeededFuture());
}
@Override
public EventBus send(String address, Object message) {
return send(address, message, new DeliveryOptions(), null);
}
@Override
public EventBus send(String address, Object message, Handler>> replyHandler) {
return send(address, message, new DeliveryOptions(), replyHandler);
}
@Override
public EventBus send(String address, Object message, DeliveryOptions options) {
return send(address, message, options, null);
}
@Override
public EventBus send(String address, Object message, DeliveryOptions options, Handler>> replyHandler) {
sendOrPubInternal(createMessage(true, address, options.getHeaders(), message, options.getCodecName()), options, replyHandler);
return this;
}
@Override
public MessageProducer sender(String address) {
Objects.requireNonNull(address, "address");
return new MessageProducerImpl<>(vertx, address, true, new DeliveryOptions());
}
@Override
public MessageProducer sender(String address, DeliveryOptions options) {
Objects.requireNonNull(address, "address");
Objects.requireNonNull(options, "options");
return new MessageProducerImpl<>(vertx, address, true, options);
}
@Override
public MessageProducer publisher(String address) {
Objects.requireNonNull(address, "address");
return new MessageProducerImpl<>(vertx, address, false, new DeliveryOptions());
}
@Override
public MessageProducer publisher(String address, DeliveryOptions options) {
Objects.requireNonNull(address, "address");
Objects.requireNonNull(options, "options");
return new MessageProducerImpl<>(vertx, address, false, options);
}
@Override
public EventBus publish(String address, Object message) {
return publish(address, message, new DeliveryOptions());
}
@Override
public EventBus publish(String address, Object message, DeliveryOptions options) {
sendOrPubInternal(createMessage(false, address, options.getHeaders(), message, options.getCodecName()), options, null);
return this;
}
@Override
public MessageConsumer consumer(String address) {
checkStarted();
Objects.requireNonNull(address, "address");
return new HandlerRegistration<>(vertx, metrics, this, address, null, false, null, -1);
}
@Override
public MessageConsumer consumer(String address, Handler> handler) {
Objects.requireNonNull(handler, "handler");
MessageConsumer consumer = consumer(address);
consumer.handler(handler);
return consumer;
}
@Override
public MessageConsumer localConsumer(String address) {
checkStarted();
Objects.requireNonNull(address, "address");
return new HandlerRegistration<>(vertx, metrics, this, address, null, true, null, -1);
}
@Override
public MessageConsumer localConsumer(String address, Handler> handler) {
Objects.requireNonNull(handler, "handler");
MessageConsumer consumer = localConsumer(address);
consumer.handler(handler);
return consumer;
}
@Override
public EventBus registerCodec(MessageCodec codec) {
codecManager.registerCodec(codec);
return this;
}
@Override
public EventBus unregisterCodec(String name) {
codecManager.unregisterCodec(name);
return this;
}
@Override
public EventBus registerDefaultCodec(Class clazz, MessageCodec codec) {
codecManager.registerDefaultCodec(clazz, codec);
return this;
}
@Override
public EventBus unregisterDefaultCodec(Class clazz) {
codecManager.unregisterDefaultCodec(clazz);
return this;
}
@Override
public void close(Handler> completionHandler) {
checkStarted();
unregisterAll();
if (metrics != null) {
metrics.close();
}
if (completionHandler != null) {
vertx.runOnContext(v -> completionHandler.handle(Future.succeededFuture()));
}
}
@Override
public boolean isMetricsEnabled() {
return metrics != null && metrics.isEnabled();
}
@Override
public EventBusMetrics getMetrics() {
return metrics;
}
protected MessageImpl createMessage(boolean send, String address, MultiMap headers, Object body, String codecName) {
Objects.requireNonNull(address, "no null address accepted");
MessageCodec codec = codecManager.lookupCodec(body, codecName);
@SuppressWarnings("unchecked")
MessageImpl msg = new MessageImpl(address, null, headers, body, codec, send, this);
return msg;
}
protected void addRegistration(String address, HandlerRegistration registration,
boolean replyHandler, boolean localOnly) {
Objects.requireNonNull(registration.getHandler(), "handler");
boolean newAddress = addLocalRegistration(address, registration, replyHandler, localOnly);
addRegistration(newAddress, address, replyHandler, localOnly, registration::setResult);
}
protected void addRegistration(boolean newAddress, String address,
boolean replyHandler, boolean localOnly,
Handler> completionHandler) {
completionHandler.handle(Future.succeededFuture());
}
protected boolean addLocalRegistration(String address, HandlerRegistration registration,
boolean replyHandler, boolean localOnly) {
Objects.requireNonNull(address, "address");
Context context = Vertx.currentContext();
boolean hasContext = context != null;
if (!hasContext) {
// Embedded
context = vertx.getOrCreateContext();
}
boolean newAddress = false;
HandlerHolder holder = new HandlerHolder<>(metrics, registration, replyHandler, localOnly, context);
Handlers handlers = handlerMap.get(address);
if (handlers == null) {
handlers = new Handlers();
Handlers prevHandlers = handlerMap.putIfAbsent(address, handlers);
if (prevHandlers != null) {
handlers = prevHandlers;
}
newAddress = true;
}
handlers.list.add(holder);
if (hasContext) {
HandlerEntry entry = new HandlerEntry<>(address, registration);
context.addCloseHook(entry);
}
return newAddress;
}
protected void removeRegistration(String address, HandlerRegistration handler, Handler> completionHandler) {
HandlerHolder holder = removeLocalRegistration(address, handler);
removeRegistration(holder, address, completionHandler);
}
protected void removeRegistration(HandlerHolder handlerHolder, String address,
Handler> completionHandler) {
callCompletionHandlerAsync(completionHandler);
}
protected HandlerHolder removeLocalRegistration(String address, HandlerRegistration handler) {
Handlers handlers = handlerMap.get(address);
HandlerHolder lastHolder = null;
if (handlers != null) {
synchronized (handlers) {
int size = handlers.list.size();
// Requires a list traversal. This is tricky to optimise since we can't use a set since
// we need fast ordered traversal for the round robin
for (int i = 0; i < size; i++) {
HandlerHolder holder = handlers.list.get(i);
if (holder.getHandler() == handler) {
handlers.list.remove(i);
holder.setRemoved();
if (handlers.list.isEmpty()) {
handlerMap.remove(address);
lastHolder = holder;
}
holder.getContext().removeCloseHook(new HandlerEntry<>(address, holder.getHandler()));
break;
}
}
}
}
return lastHolder;
}
protected void sendReply(MessageImpl replyMessage, MessageImpl replierMessage, DeliveryOptions options,
Handler>> replyHandler) {
if (replyMessage.address() == null) {
throw new IllegalStateException("address not specified");
} else {
HandlerRegistration replyHandlerRegistration = createReplyHandlerRegistration(replyMessage, options, replyHandler);
new ReplySendContextImpl<>(replyMessage, options, replyHandlerRegistration, replierMessage).next();
}
}
protected void sendReply(SendContextImpl sendContext, MessageImpl replierMessage) {
sendOrPub(sendContext);
}
protected void sendOrPub(SendContextImpl sendContext) {
MessageImpl message = sendContext.message;
metrics.messageSent(message.address(), !message.send(), true, false);
deliverMessageLocally(sendContext);
}
protected Handler> convertHandler(Handler>> handler) {
return reply -> {
Future> result;
if (reply.body() instanceof ReplyException) {
// This is kind of clunky - but hey-ho
ReplyException exception = (ReplyException) reply.body();
metrics.replyFailure(reply.address(), exception.failureType());
result = Future.failedFuture(exception);
} else {
result = Future.succeededFuture(reply);
}
handler.handle(result);
};
}
protected void callCompletionHandlerAsync(Handler> completionHandler) {
if (completionHandler != null) {
vertx.runOnContext(v -> completionHandler.handle(Future.succeededFuture()));
}
}
protected void deliverMessageLocally(SendContextImpl sendContext) {
if (!deliverMessageLocally(sendContext.message)) {
// no handlers
metrics.replyFailure(sendContext.message.address, ReplyFailure.NO_HANDLERS);
if (sendContext.handlerRegistration != null) {
sendContext.handlerRegistration.sendAsyncResultFailure(ReplyFailure.NO_HANDLERS, "No handlers for address "
+ sendContext.message.address);
}
}
}
protected boolean isMessageLocal(MessageImpl msg) {
return true;
}
protected boolean deliverMessageLocally(MessageImpl msg) {
msg.setBus(this);
Handlers handlers = handlerMap.get(msg.address());
if (handlers != null) {
if (msg.send()) {
//Choose one
HandlerHolder holder = handlers.choose();
if (holder != null) {
metrics.messageReceived(msg.address(), !msg.send(), isMessageLocal(msg), 1);
deliverToHandler(msg, holder);
}
} else {
// Publish
metrics.messageReceived(msg.address(), !msg.send(), isMessageLocal(msg), handlers.list.size());
for (HandlerHolder holder: handlers.list) {
deliverToHandler(msg, holder);
}
}
return true;
} else {
metrics.messageReceived(msg.address(), !msg.send(), isMessageLocal(msg), 0);
return false;
}
}
protected void checkStarted() {
if (!started) {
throw new IllegalStateException("Event Bus is not started");
}
}
protected String generateReplyAddress() {
return Long.toString(replySequence.incrementAndGet());
}
private HandlerRegistration createReplyHandlerRegistration(MessageImpl message,
DeliveryOptions options,
Handler>> replyHandler) {
if (replyHandler != null) {
long timeout = options.getSendTimeout();
String replyAddress = generateReplyAddress();
message.setReplyAddress(replyAddress);
Handler> simpleReplyHandler = convertHandler(replyHandler);
HandlerRegistration registration =
new HandlerRegistration<>(vertx, metrics, this, replyAddress, message.address, true, replyHandler, timeout);
registration.handler(simpleReplyHandler);
return registration;
} else {
return null;
}
}
private void sendOrPubInternal(MessageImpl message, DeliveryOptions options,
Handler>> replyHandler) {
checkStarted();
HandlerRegistration replyHandlerRegistration = createReplyHandlerRegistration(message, options, replyHandler);
SendContextImpl sendContext = new SendContextImpl<>(message, options, replyHandlerRegistration);
sendContext.next();
}
protected class SendContextImpl implements SendContext {
public final MessageImpl message;
public final DeliveryOptions options;
public final HandlerRegistration handlerRegistration;
public final Iterator> iter;
public SendContextImpl(MessageImpl message, DeliveryOptions options, HandlerRegistration handlerRegistration) {
this.message = message;
this.options = options;
this.handlerRegistration = handlerRegistration;
this.iter = interceptors.iterator();
}
@Override
public Message message() {
return message;
}
@Override
public void next() {
if (iter.hasNext()) {
Handler handler = iter.next();
try {
handler.handle(this);
} catch (Throwable t) {
log.error("Failure in interceptor", t);
}
} else {
sendOrPub(this);
}
}
@Override
public boolean send() {
return message.send();
}
}
protected class ReplySendContextImpl extends SendContextImpl {
private final MessageImpl replierMessage;
public ReplySendContextImpl(MessageImpl message, DeliveryOptions options, HandlerRegistration handlerRegistration,
MessageImpl replierMessage) {
super(message, options, handlerRegistration);
this.replierMessage = replierMessage;
}
@Override
public void next() {
if (iter.hasNext()) {
Handler handler = iter.next();
handler.handle(this);
} else {
sendReply(this, replierMessage);
}
}
}
private void unregisterAll() {
// Unregister all handlers explicitly - don't rely on context hooks
for (Handlers handlers: handlerMap.values()) {
for (HandlerHolder holder: handlers.list) {
holder.getHandler().unregister(true);
}
}
}
private void deliverToHandler(MessageImpl msg, HandlerHolder holder) {
// Each handler gets a fresh copy
@SuppressWarnings("unchecked")
Message copied = msg.copyBeforeReceive();
holder.getContext().runOnContext((v) -> {
// Need to check handler is still there - the handler might have been removed after the message were sent but
// before it was received
try {
if (!holder.isRemoved()) {
holder.getHandler().handle(copied);
}
} finally {
if (holder.isReplyHandler()) {
holder.getHandler().unregister();
}
}
});
}
public class HandlerEntry implements Closeable {
final String address;
final HandlerRegistration handler;
public HandlerEntry(String address, HandlerRegistration handler) {
this.address = address;
this.handler = handler;
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (getClass() != o.getClass()) return false;
HandlerEntry entry = (HandlerEntry) o;
if (!address.equals(entry.address)) return false;
if (!handler.equals(entry.handler)) return false;
return true;
}
@Override
public int hashCode() {
int result = address != null ? address.hashCode() : 0;
result = 31 * result + (handler != null ? handler.hashCode() : 0);
return result;
}
// Called by context on undeploy
public void close(Handler> completionHandler) {
handler.unregister(completionHandler);
completionHandler.handle(Future.succeededFuture());
}
}
@Override
protected void finalize() throws Throwable {
// Make sure this gets cleaned up if there are no more references to it
// so as not to leave connections and resources dangling until the system is shutdown
// which could make the JVM run out of file handles.
close(ar -> {});
super.finalize();
}
}