io.vlingo.xoom.actors.Router Maven / Gradle / Ivy
// Copyright © 2012-2021 VLINGO LABS. All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL
// was not distributed with this file, You can obtain
// one at https://mozilla.org/MPL/2.0/.
package io.vlingo.xoom.actors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import io.vlingo.xoom.common.Completes;
import io.vlingo.xoom.common.PentaConsumer;
import io.vlingo.xoom.common.PentaFunction;
import io.vlingo.xoom.common.QuadConsumer;
import io.vlingo.xoom.common.QuadFunction;
import io.vlingo.xoom.common.TriConsumer;
import io.vlingo.xoom.common.TriFunction;
/**
* Router is a kind of {@link Actor} that forwards a message
* to one or more other {@link Actor actors} according to a
* computed {@link Routing}.
*/
public abstract class Router extends Actor {
protected final List> routees;
public Router(final RouterSpecification specification) {
this.routees = new ArrayList>();
initRoutees(specification);
}
protected void initRoutees(final RouterSpecification specification) {
for (int i = 0; i < specification.initialPoolSize(); i++) {
final Class>[] protocols = new Class>[] { specification.routerProtocol(), Addressable.class };
final Protocols two = childActorFor(protocols, specification.routerDefinition());
subscribe(Routee.of(two.get(0), two.get(1)));
}
}
protected List> routees() {
return Collections.unmodifiableList(routees);
}
public Routee routeeAt(int index) {
return (index < 0 || index >= routees.size()) ? null : routees.get(index);
}
//SUBSCRIPTION
protected void subscribe(Routee
routee) {
if (!routees.contains(routee))
routees.add(routee);
}
protected void subscribe(List> toSubscribe) {
toSubscribe.forEach(routee -> subscribe(routee));
}
protected void unsubscribe(Routee routee) {
routees.remove(routee);
}
protected void unsubscribe(List> toUnsubscribe) {
toUnsubscribe.forEach(routee -> unsubscribe(routee));
}
//ROUTE COMPUTATION
protected abstract Routing computeRouting();
protected Routing routingFor(final T1 routable1) {
/* by default, assume the routing is not dependent on message content */
return computeRouting();
}
protected Routing routingFor(final T1 routable1, final T2 routable2) {
/* by default, assume the routing is not dependent on message content */
return computeRouting();
}
protected Routing routingFor(final T1 routable1, final T2 routable2, final T3 routable3) {
/* by default, assume the routing is not dependent on message content */
return computeRouting();
}
protected Routing routingFor(final T1 routable1, final T2 routable2, final T3 routable3, final T4 routable4) {
/* by default, assume the routing is not dependent on message content */
return computeRouting();
}
//DISPATCHING - COMMANDS
protected void dispatchCommand(final BiConsumer action, final T1 routable1) {
routingFor(routable1)
.routees()
.forEach(routee -> routee.receiveCommand(action, routable1));
}
protected void dispatchCommand(final TriConsumer action, final T1 routable1, final T2 routable2) {
routingFor(routable1, routable2)
.routees()
.forEach(routee -> routee.receiveCommand(action, routable1, routable2));
}
protected void dispatchCommand(final QuadConsumer action, final T1 routable1, final T2 routable2, final T3 routable3) {
routingFor(routable1, routable2, routable3)
.routees()
.forEach(routee -> routee.receiveCommand(action, routable1, routable2, routable3));
}
protected void dispatchCommand(final PentaConsumer action, final T1 routable1, final T2 routable2, final T3 routable3, final T4 routable4) {
routingFor(routable1, routable2, routable3, routable4)
.routees()
.forEach(routee -> routee.receiveCommand(action, routable1, routable2, routable3, routable4));
}
//DISPATCHING - QUERIES
@SuppressWarnings("unchecked")
protected > R dispatchQuery(final BiFunction query, final T1 routable1) {
final CompletesEventually completesEventually = completesEventually();
routingFor(routable1)
.first() //by default, for protocols with a return value, route only to first routee
.receiveQuery(query, routable1)
.andFinallyConsume(completesEventually::with);
return (R) completes(); //this is a fake out; the real completes doesn't happen until inside the lambda
}
@SuppressWarnings("unchecked")
protected > R dispatchQuery(final TriFunction query, final T1 routable1, final T2 routable2) {
final CompletesEventually completesEventually = completesEventually();
routingFor(routable1, routable2)
.first() //by default, for protocols with a return value, route only to first routee
.receiveQuery(query, routable1, routable2)
.andFinallyConsume(completesEventually::with);
return (R) completes(); //this is a fake out; the real completes doesn't happen until inside the lambda
}
@SuppressWarnings("unchecked")
protected > R dispatchQuery(final QuadFunction query, final T1 routable1, final T2 routable2, final T3 routable3) {
final CompletesEventually completesEventually = completesEventually();
routingFor(routable1, routable2)
.first() //by default, for protocols with a return value, route only to first routee
.receiveQuery(query, routable1, routable2, routable3)
.andFinallyConsume(completesEventually::with);
return (R) completes(); //this is a fake out; the real completes doesn't happen until inside the lambda
}
@SuppressWarnings("unchecked")
protected > R dispatchQuery(final PentaFunction query, final T1 routable1, final T2 routable2, final T3 routable3, final T4 routable4) {
final CompletesEventually completesEventually = completesEventually();
routingFor(routable1, routable2)
.first() //by default, for protocols with a return value, route only to first routee
.receiveQuery(query, routable1, routable2, routable3, routable4)
.andFinallyConsume(completesEventually::with);
return (R) completes(); //this is a fake out; the real completes doesn't happen until inside the lambda
}
}