/** * Sends a request message to an actor, awaits a response value (but no longer than the given * timeout) and returns it. This method can be called by any code, even non-actor code. If the * actor responds with an error message, a {@link RuntimeException} will be thrown by this method. * <br> * The message's {@code id} and {@code from} properties may be left unset. * * <p>This method should be used as in the following example (assuming a {@code String} return * value: * * <pre>{@code * String res = call(actor, new MyRequest()); * }</pre> * * In the example, {@code MyRequest} extends {@link RequestMessage}. Note how the result of the * {@link #from() from} method is passed to the request's constructor, but the message ID isn't. * * @param <V> the return value's type * @param actor the actor to which the request is sent * @param timeout the maximum duration to wait for a response * @param unit the time unit of the timeout * @return the value sent by the actor as a response * @throws RuntimeException if the actor responds with an error message, its contained exception * will be thrown, possibly wrapped by a {@link RuntimeException}. * @throws TimeoutException if the timeout expires before a response is received from the actor. * @throws InterruptedException */ public static <V> V call(final ActorRef actor, RequestMessage<V> m, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException, SuspendExecution { assert !actor.equals(LocalActor.self()) : "Can't \"call\" self - deadlock guaranteed"; if (m.getFrom() == null) m.setFrom(from()); final Actor currentActor; if (m.getFrom() instanceof TempActor) currentActor = ((TempActor<?>) m.getFrom()).actor.get(); else currentActor = Actor.currentActor(); assert currentActor != null; final Object watch = currentActor.watch(actor); if (m.getId() == null) m.setId(watch); final Object id = m.getId(); final SelectiveReceiveHelper<Object> helper = new SelectiveReceiveHelper<Object>(currentActor) { @Override protected void handleLifecycleMessage(LifecycleMessage m) { if (m instanceof ExitMessage) { final ExitMessage exit = (ExitMessage) m; if (Objects.equals(exit.getActor(), actor) && exit.getWatch() == watch) throw Exceptions.rethrow(exit.getCause()); } super.handleLifecycleMessage(m); } }; try { actor.sendSync(m); final ResponseMessage response = (ResponseMessage) helper.receive( timeout, unit, new MessageProcessor<Object, Object>() { @Override public Object process(Object m) throws SuspendExecution, InterruptedException { return (m instanceof ResponseMessage && id.equals(((ResponseMessage) m).getId())) ? m : null; } }); currentActor.unwatch( actor, watch); // no need to unwatch in case of receiver death, so not doen in finally block if (response instanceof ErrorResponseMessage) throw Exceptions.rethrow(((ErrorResponseMessage) response).getError()); return ((ValueResponseMessage<V>) response).getValue(); } finally { if (m.getFrom() instanceof TempActor) ((TempActor) m.getFrom()).done(); } }
@SuppressWarnings("unchecked") private Pair<ActorRef<? extends WebMessage>, Class<? extends ActorImpl<? extends WebMessage>>> autoCreateActor(FullHttpRequest req) { registerActorClasses(); final String uri = req.getUri(); for (final Class<?> c : actorClasses) { if (WebActorHandler.handlesWithHttp(uri, c) || WebActorHandler.handlesWithWebSocket(uri, c)) return new Pair< ActorRef<? extends WebMessage>, Class<? extends ActorImpl<? extends WebMessage>>>( Actor.newActor( new ActorSpec( c, actorParams != null ? actorParams.get(c) : EMPTY_OBJECT_ARRAY)) .spawn(), (Class<? extends ActorImpl<? extends WebMessage>>) c); } return null; }