org.praxislive.base.AbstractAsyncControl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of praxiscore-base Show documentation
Show all versions of praxiscore-base Show documentation
Forest-of-actors runtime supporting real-time systems and real-time recoding - bringing aspects of Erlang, Smalltalk and Extempore to Java.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2023 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License version 3
* along with this work; if not, see http://www.gnu.org/licenses/
*
*
* Please visit https://www.praxislive.org if you need additional information or
* have any questions.
*/
package org.praxislive.base;
import java.util.LinkedList;
import java.util.Queue;
import org.praxislive.core.Call;
import org.praxislive.core.Control;
import org.praxislive.core.PacketRouter;
import org.praxislive.core.types.PError;
/**
*
*/
public abstract class AbstractAsyncControl implements Control {
private final static System.Logger LOG = System.getLogger(AbstractAsyncControl.class.getName());
private final Queue callQueue;
private Call pending;
protected AbstractAsyncControl() {
callQueue = new LinkedList<>();
}
@Override
public void call(Call call, PacketRouter router) throws Exception {
if (call.isRequest()) {
processInvoke(call, router);
} else {
processResponse(call, router);
}
}
protected Call getActiveCall() {
return callQueue.peek();
}
protected abstract Call processInvoke(Call call) throws Exception;
protected abstract Call processResponse(Call call) throws Exception;
protected Call processError(Call call) throws Exception {
return getActiveCall().error(call.args());
}
private void processInvoke(Call call, PacketRouter router) {
if (callQueue.isEmpty()) {
callQueue.add(call);
doInvokeLoop(router);
} else {
callQueue.add(call);
}
}
private void processResponse(Call call, PacketRouter router) {
if (pending == null || pending.matchID() != call.matchID()) {
LOG.log(System.Logger.Level.WARNING ,"Unexpected call received by processResponse(call, router)");
return;
}
pending = null;
try {
Call ret;
if (call.isError()) {
ret = processError(call);
} else {
ret = processResponse(call);
}
if (ret.isRequest()) {
pending = ret;
router.route(ret);
return;
} else {
Call active = callQueue.peek();
if (active.matchID() != ret.matchID()) {
throw new IllegalStateException();
}
callQueue.poll();
router.route(ret);
}
} catch (Exception ex) {
Call active = callQueue.poll();
if (active != null) {
router.route(active.error(PError.of(ex)));
}
}
doInvokeLoop(router);
}
private void doInvokeLoop(PacketRouter router) {
while (!callQueue.isEmpty()) {
Call call = callQueue.peek();
try {
Call ret = processInvoke(call);
if (ret.isRequest()) {
if (!ret.isReplyRequired()) {
throw new IllegalStateException("processInvoke(call) returned illegal quiet call");
}
pending = ret;
router.route(ret);
return;
} else {
if (ret.matchID() != call.matchID()) {
throw new IllegalStateException("processInvoke(call) returned non-matching response call");
}
callQueue.poll();
router.route(ret);
}
} catch (Exception ex) {
LOG.log(System.Logger.Level.DEBUG, "Exception thrown processing call", ex);
callQueue.poll();
router.route(call.error(PError.of(ex)));
}
}
}
}