
vertx.effect.VertxRef Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vertx-effect Show documentation
Show all versions of vertx-effect Show documentation
When actors meet Functional Programming
package vertx.effect;
import io.vertx.core.*;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.eventbus.ReplyException;
import vertx.effect.core.EventPublisher;
import vertx.effect.core.Functions;
import vertx.effect.core.MyVerticle;
import vertx.effect.exp.Cons;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import static io.vertx.core.eventbus.ReplyFailure.RECIPIENT_FAILURE;
import static java.util.Objects.requireNonNull;
import static vertx.effect.Failures.INTERNAL_ERROR_CODE;
import static vertx.effect.Failures.UNKNOWN_ERROR_CODE;
/**
Wrapper around the vertx instance. It deploys and spawns functions and tasks as Verticles.
*/
public class VertxRef {
public static final String EVENTS_ADDRESS = "vertx-values-events";
private static final DeploymentOptions DEFAULT_OPTIONS = new DeploymentOptions();
private static final AtomicLong processSeq = new AtomicLong(0);
private final Vertx vertx;
private final DeploymentOptions deploymentOptions;
private static final Function deliveryOpt = multimap -> new DeliveryOptions().setHeaders(multimap);
private static final String ADDRESS_IS_NULL = "address is null";
private static final String CONSUMER_IS_NULL = "consumer is null";
private static final String OPTIONS_IS_NULL = "options is null";
private static final String LAMBDA_IS_NULL = "λ is null";
private static final String LAMBDAC_IS_NULL = "λc is null";
private static final String VERTICLE_IS_NULL = "verticle is null";
/**
Creates a factory to deploy and spawn verticles
@param vertx the vertx instance
*/
public VertxRef(final Vertx vertx) {
this(requireNonNull(vertx),
DEFAULT_OPTIONS
);
}
/**
Creates a factory to deploy and spawn verticles
@param vertx the vertx instance
@param deploymentOptions the default deployment options that will be used for deploying and spawning
verticles if one is not provided
*/
public VertxRef(final Vertx vertx,
final DeploymentOptions deploymentOptions
) {
this.vertx = requireNonNull(vertx);
this.deploymentOptions = requireNonNull(deploymentOptions);
}
/**
@param address the address of the verticle
@param consumer the consumer that will process the messages sent to the verticle
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a future
*/
public Val> deployConsumer(final String address,
final Consumer> consumer
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (consumer == null) return Cons.failure(new NullPointerException(CONSUMER_IS_NULL));
return deployConsumer(address,
consumer,
deploymentOptions
);
}
/**
@param address the address of the verticle
@param consumer the consumer that will process the messages sent to the verticle
@param options options for configuring the verticle deployment
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a future
*/
public Val> deployConsumer(final String address,
final Consumer> consumer,
final DeploymentOptions options
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (consumer == null) return Cons.failure(new NullPointerException(CONSUMER_IS_NULL));
if (options == null) return Cons.failure(new NullPointerException(OPTIONS_IS_NULL));
final int instances = options.getInstances();
final Set ids = new HashSet<>();
@SuppressWarnings({"rawtypes", "squid:S3740"}) final List futures = new ArrayList<>();
final MyVerticle verticle = new MyVerticle<>(consumer,
address
);
return Cons.of(() -> {
for (int i = 0; i < instances; i++) {
final Future future = vertx.deployVerticle(verticle,
options.setInstances(1)
);
futures.add(future.onSuccess(id -> {
ids.add(id);
EventPublisher.PUBLISHER.deployedVerticle(address,
id
)
.accept(vertx);
}
));
}
return CompositeFuture.all(futures)
.flatMap(cf -> Future.succeededFuture(new VerticleRef<>(vertx,
ids,
address
)
)
);
}
);
}
/**
@param address the address of the verticle
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a future
*/
public Val> deploy(final String address,
final λ lambda
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (lambda == null) return Cons.failure(new NullPointerException(LAMBDA_IS_NULL));
return deploy(address,
lambda,
deploymentOptions
);
}
/**
@param address the address of the verticle
@param lambda the function that takes a message of type I and produces an output of type O
@param options options for configuring the verticle deployment
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a Val
*/
public Val> deploy(final String address,
final λ lambda,
final DeploymentOptions options
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (lambda == null) return Cons.failure(new NullPointerException(LAMBDA_IS_NULL));
if (options == null) return Cons.failure(new NullPointerException(OPTIONS_IS_NULL));
return Cons.of(() -> {
final int instances = options.getInstances();
final Set ids = new HashSet<>();
@SuppressWarnings({"rawtypes", "squid:S3740"}) final List futures = new ArrayList<>();
final MyVerticle verticle = new MyVerticle<>(message -> wrapLambda(address,
message,
lambda
),
address
);
for (int i = 0; i < instances; i++) {
final Future future = vertx.deployVerticle(verticle,
options.setInstances(1)
);
futures.add(future.onSuccess(id -> {
ids.add(id);
EventPublisher.PUBLISHER.deployedVerticle(address,
id
)
.accept(vertx);
}
));
}
return CompositeFuture.all(futures)
.flatMap(cf -> Future.succeededFuture(new VerticleRef<>(vertx,
ids,
address
)
)
);
}
);
}
/**
@param address the address of the verticle
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a Val
*/
public Val> deploy(final String address,
final λc lambda
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (lambda == null) return Cons.failure(new NullPointerException(LAMBDA_IS_NULL));
return deploy(address,
lambda,
deploymentOptions
);
}
/**
@param address the address of the verticle
@param lambda the function that takes a message of type I and produces an output of type O
@param options options for configuring the verticle deployment
@param the type of the message sent to the verticle
@param the type of the reply
@return an VerticleRel wrapped in a Val
*/
public Val> deploy(final String address,
final λc lambda,
final DeploymentOptions options
) {
if (address == null) return Cons.failure(new NullPointerException(ADDRESS_IS_NULL));
if (lambda == null) return Cons.failure(new NullPointerException(LAMBDAC_IS_NULL));
if (options == null) return Cons.failure(new NullPointerException(OPTIONS_IS_NULL));
return Cons.of(() -> {
final int instances = options.getInstances();
final Set ids = new HashSet<>();
@SuppressWarnings({"rawtypes", "squid:S3740"}) final List futures = new ArrayList<>();
final MyVerticle verticle = new MyVerticle<>(message -> wrapLambda(address,
message,
lambda
),
address
);
for (int i = 0; i < instances; i++) {
final Future future = vertx.deployVerticle(verticle,
options.setInstances(1)
);
futures.add(future.onSuccess(id -> {
ids.add(id);
EventPublisher.PUBLISHER.deployedVerticle(address,
id
)
.accept(vertx);
}
));
}
return CompositeFuture.all(futures)
.flatMap(cf -> Future.succeededFuture(new VerticleRef<>(vertx,
ids,
address
)
)
);
}
);
}
/**
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@param address the prefix of the auto generated address
@return an VerticleRel wrapped in a Val
*/
public λ spawn(final String address,
final λ lambda) {
return spawn(address,
lambda,
deploymentOptions
);
}
/**
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@param address the prefix of the auto generated address
@return an VerticleRel wrapped in a Val
*/
public λc spawn(final String address,
final λc lambda) {
return spawn(address,
lambda,
deploymentOptions
);
}
/**
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@param address the prefix of the auto generated address
@param options the deployment options
@return an VerticleRel wrapped in a Val
*/
public λc spawn(final String address,
final λc lambda,
final DeploymentOptions options) {
requireNonNull(address);
requireNonNull(options);
requireNonNull(lambda);
return (context, input) ->
{
String generatedAddress = generateProcessAddress(address);
Consumer> consumer = message -> wrapLambda(generatedAddress,
message,
lambda
);
Val> future = deployConsumer(generatedAddress,
consumer,
options
);
return future.flatMap(r -> r.trace()
.apply(context,
input
)
.onComplete(__ -> r.undeploy()
.onComplete(event -> {
if (event.succeeded())
EventPublisher.PUBLISHER.undeployedVerticle(generatedAddress)
.accept(vertx);
else
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_UNDEPLOYING_VERTICLE,
generatedAddress,
event.cause()
)
.accept(vertx);
})
)
);
};
}
/**
@param lambda the function that takes a message of type I and produces an output of type O
@param the type of the message sent to the verticle
@param the type of the reply
@param address the prefix of the auto generated address
@param options the deployment options
@return an VerticleRel wrapped in a Val
*/
public λ spawn(final String address,
final λ lambda,
final DeploymentOptions options) {
requireNonNull(address);
requireNonNull(options);
requireNonNull(lambda);
return input ->
{
String generatedAddress = generateProcessAddress(address);
Consumer> consumer = message -> wrapLambda(generatedAddress,
message,
lambda
);
Val> future = deployConsumer(generatedAddress,
consumer,
options
);
return future.flatMap(r -> r.ask()
.apply(input)
.onComplete(__ -> r.undeploy()
.onComplete(event -> {
if (event.succeeded())
EventPublisher.PUBLISHER.undeployedVerticle(generatedAddress)
.accept(vertx);
else
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_UNDEPLOYING_VERTICLE,
generatedAddress,
event.cause()
)
.accept(vertx);
})
)
);
};
}
public Val deployVerticle(final AbstractVerticle verticle,
final DeploymentOptions options) {
if (verticle == null) return Cons.failure(new NullPointerException(VERTICLE_IS_NULL));
if (options == null) return Cons.failure(new NullPointerException(OPTIONS_IS_NULL));
return Cons.of(() -> vertx.deployVerticle(verticle,
options
)
.onComplete(event -> {
if (event.succeeded())
EventPublisher.PUBLISHER.deployedVerticle(verticle.getClass(),
event.result()
)
.accept(vertx);
else
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_DEPLOYING_VERTICLE,
verticle.getClass(),
event.cause()
)
.accept(vertx);
}
)
);
}
public Val deployVerticle(final AbstractVerticle verticle) {
return deployVerticle(verticle,
deploymentOptions
);
}
public Consumer registerPublisher(final String address) {
requireNonNull(address);
if (Objects.equals(address,
EVENTS_ADDRESS
))
return message -> vertx.eventBus()
.publish(address,
message
);
else
return message -> {
EventPublisher.PUBLISHER.sentMessage(address,
message
)
.accept(vertx);
vertx.eventBus()
.publish(address,
message
);
};
}
public MessageConsumer registerConsumer(final String address,
final Consumer consumer) {
requireNonNull(address);
requireNonNull(consumer);
if (!Objects.equals(address,
EVENTS_ADDRESS
)) {
return vertx.eventBus()
.consumer(address,
message -> {
try {
EventPublisher.PUBLISHER.receivedMessage(address,
message.headers()
)
.accept(vertx);
O body = message.body();
consumer.accept(body);
message.reply(null);
} catch (Exception exc) {
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_PROCESSING_MESSAGE,
address,
exc,
message.headers()
)
.accept(vertx);
message.reply(new ReplyException(RECIPIENT_FAILURE,
INTERNAL_ERROR_CODE,
Functions.getErrorMessage(exc)
));
}
}
);
}
else return vertx.eventBus()
.consumer(address,
message -> {
try {
O body = message.body();
consumer.accept(body);
message.reply(null);
} catch (Exception exc) {
message.reply(new ReplyException(RECIPIENT_FAILURE,
INTERNAL_ERROR_CODE,
Functions.getErrorMessage(exc)
));
}
}
);
}
public Val delay(final int time,
final TimeUnit unit) {
if (time < 0) return Cons.failure(new IllegalArgumentException("time < 0"));
if (unit == null) return Cons.failure(new NullPointerException("unit is null"));
return Cons.of(() -> {
EventPublisher.PUBLISHER.timer(Event.TIMER_STARTS_EVENT)
.accept(vertx);
Promise promise = Promise.promise();
vertx.setTimer(requireNonNull(unit).toMillis(time),
result -> {
promise.complete(null);
EventPublisher.PUBLISHER.timer(Event.TIMER_ENDS_EVENT)
.accept(vertx);
}
);
return promise.future();
});
}
private static String generateProcessAddress(final String address) {
return String.format("spawned.%s.%s",
address,
processSeq.getAndIncrement()
);
}
private void wrapLambda(final String address,
final Message message,
final λ fn) {
MultiMap headers = message.headers();
try {
EventPublisher.PUBLISHER.receivedMessage(address,
headers
)
.accept(vertx);
fn.apply(message.body())
.onComplete(event -> {
if (event.succeeded()) {
message.reply(event.result(),
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.repliedResp(address,
event.result(),
headers
)
.accept(vertx);
}
else {
ReplyException error = Failures.REPLY_EXCEPTION_PRISM
.getOptional.apply(event.cause())
.orElse(new ReplyException(RECIPIENT_FAILURE,
UNKNOWN_ERROR_CODE,
Functions.getErrorMessage(event.cause())
));
message.reply(error,
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.repliedError(address,
error,
headers
)
.accept(vertx);
}
})
.get();
} catch (Exception exc) {
message.reply(new ReplyException(RECIPIENT_FAILURE,
INTERNAL_ERROR_CODE,
Functions.getErrorMessage(exc)
),
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_PROCESSING_MESSAGE,
address,
exc,
headers
)
.accept(vertx);
}
}
private void wrapLambda(final String address,
final Message message,
final λc fn) {
MultiMap headers = message.headers();
try {
EventPublisher.PUBLISHER.receivedMessage(address,
message.headers()
)
.accept(vertx);
fn.apply(headers,
message.body()
)
.onComplete(event -> {
if (event.succeeded()) {
message.reply(event.result(),
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.repliedResp(address,
event.result(),
headers
)
.accept(vertx);
}
else {
ReplyException error = Failures
.REPLY_EXCEPTION_PRISM
.getOptional.apply(event.cause())
.orElse(new ReplyException(RECIPIENT_FAILURE,
UNKNOWN_ERROR_CODE,
Functions.getErrorMessage(event.cause())
));
message.reply(error,
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.repliedError(address,
error,
headers
)
.accept(vertx);
}
})
.get();
} catch (Exception exc) {
message.reply(new ReplyException(RECIPIENT_FAILURE,
INTERNAL_ERROR_CODE,
Functions.getErrorMessage(exc)
),
deliveryOpt.apply(headers)
);
EventPublisher.PUBLISHER.internalError(Event.INTERNAL_ERROR_PROCESSING_MESSAGE,
address,
exc,
message.headers()
)
.accept(vertx);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy