@Override
  protected CompletionStage<Collection<JM>> handleMessageAsync(M message, MessageCollector collector,
      TaskCoordinator coordinator) {
    if (message == null) {
      return CompletableFuture.completedFuture(Collections.emptyList());
    }

    K key = joinOpSpec.getJoinFn().getMessageKey(message);
    Object[] args = joinOpSpec.getArgs();

    return Optional.ofNullable(key)
        .map(joinKey -> table.getAsync(joinKey, args)
            .thenApply(val -> getJoinOutput(joinKey, val, message)))
        .orElseGet(() -> CompletableFuture.completedFuture(getJoinOutput(key, null, message)));
  }
Implementation of a stream-table join operator that first retrieve the value of
the message key from incoming message, and then apply the join function.

@param <K> type of the join key
@param <M> type of input messages
@param <R> type of the table record
@param <JM> type of the join result
samza-core/src/main/java/org/apache/samza/operators/impl/StreamTableJoinOperatorImpl.java