net.tangly.fsm.actors.Channel Maven / Gradle / Ivy
/*
* Copyright 2023-2024 Marcel Baumann
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* https://apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*
*/
package net.tangly.fsm.actors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
/**
* Provide a subscription handler for multiple suppliers and consumers. The type of handled messages is a generic parameter and provides type security.
* The implementation uses the flow interface of the standard Java API.
* The channel provides the communication medium between publishers and subscribers.
* Subscribers are asynchronous objects.
*
* @param type of the messages handled by the channel
*/
public class Channel implements AutoCloseable {
private final String name;
private final SubmissionPublisher publisher;
static class ActorSubscriber implements Flow.Subscriber {
private static final Logger logger = LogManager.getLogger();
private final Actor actor;
private final Channel channel;
private Flow.Subscription subscription;
public ActorSubscriber(@NotNull Actor actor, @NotNull Channel channel) {
this.actor = actor;
this.channel = channel;
}
@Override
public void onSubscribe(@NotNull Flow.Subscription subscription) {
logger.atInfo().log("actor {} is subscribed to channel {}", actor.name(), channel.name());
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(@NotNull T message) {
logger.atInfo().log("actor {} received message {} from channel {}", actor.name(), message, channel.name());
actor.receive(message);
subscription.request(1);
}
@Override
public void onError(Throwable e) {
logger.atError().withThrowable(e).log("actor {} has error on channel {}", actor.name(), channel.name());
}
@Override
public void onComplete() {
logger.atInfo().log("actor {} has completed on channel {}", actor.name(), channel.name());
}
}
public Channel(@NotNull String name) {
this.name = name;
publisher = new SubmissionPublisher<>();
}
public String name() {
return name;
}
public SubmissionPublisher publisher() {
return publisher;
}
/**
* Subscribe an actor to the channel. The actor will receive messages from the channel.
* @param actor actor to subscribe
*/
public void subscribe(@NotNull Actor actor) {
publisher.subscribe(new ActorSubscriber<>(actor, this));
}
/**
* Dispatch a message to all subscribers of the channel.
* @param message message to dispatch
*/
public void publish(@NotNull T message) {
publisher.submit(message);
}
@Override
public void close() {
publisher.close();
}
}