@Override
  public boolean hasNext() {
    while (next == null && toConsumeEvents.size() > 0) {
      WindowEvent toConsumeEvent = toConsumeEvents.removeFirst();
      String consumedId = toConsumeEvent.getWindowId();

      //
      PortletInfo consumerPortletInfo = context.getPortletInfo(consumedId);
      if (consumerPortletInfo == null) {
        log.trace(
            "Cannot deliver event "
                + toConsumeEvent
                + " because the consumer of the event does not have a portlet info");
        safeInvoker.eventDiscarded(
            context.getEventControllerContext(),
            this,
            toConsumeEvent,
            EventControllerContext.EVENT_CONSUMER_INFO_NOT_AVAILABLE);
        continue;
      }

      //
      if (!controller.getDistributeNonConsumableEvents()) {
        if (!consumerPortletInfo
            .getEventing()
            .getConsumedEvents()
            .containsKey(toConsumeEvent.getName())) {
          log.trace(
              "Cannot deliver event "
                  + toConsumeEvent
                  + " because the consumer of the event does not accept the event name");
          safeInvoker.eventDiscarded(
              context.getEventControllerContext(),
              this,
              toConsumeEvent,
              EventControllerContext.PORTLET_DOES_NOT_CONSUME_EVENT);
          continue;
        }
      }

      //
      next = toConsumeEvent;
    }

    //
    return next != null;
  }
  boolean push(WindowEvent consumedEvent, WindowEvent producedEvent) {
    String producerId = producedEvent.getWindowId();
    PortletInfo producerPortletInfo = context.getPortletInfo(producerId);

    //
    if (producerPortletInfo == null) {
      log.trace(
          "Cannot deliver event "
              + producedEvent
              + " because the producer does not have portlet info");
      safeInvoker.eventDiscarded(
          context.getEventControllerContext(),
          this,
          producedEvent,
          EventControllerContext.EVENT_PRODUCER_INFO_NOT_AVAILABLE);
      return true;
    } else {
      //
      if (!controller.getDistributeNonProduceableEvents()) {
        if (!producerPortletInfo
            .getEventing()
            .getProducedEvents()
            .containsKey(producedEvent.getName())) {
          log.trace(
              "Cannot deliver event "
                  + producedEvent
                  + " because the producer of the event does not produce the event name");
          safeInvoker.eventDiscarded(
              context.getEventControllerContext(),
              this,
              producedEvent,
              EventControllerContext.PORTLET_DOES_NOT_CONSUME_EVENT);
          return true;
        }
      }

      // Apply produced event quota if necessary
      int producedEventThreshold = controller.getProducedEventThreshold();
      if (producedEventThreshold >= 0) {
        if (producedEventSize + 1 > producedEventThreshold) {
          log.trace(
              "Event distribution interrupted because the maximum number of produced event is reached");
          safeInvoker.eventDiscarded(
              context.getEventControllerContext(),
              this,
              producedEvent,
              EventControllerContext.PRODUCED_EVENT_FLOODED);
          return false;
        }
      }

      //
      Iterable<WindowEvent> toConsume =
          safeInvoker.eventProduced(
              context.getEventControllerContext(), this, consumedEvent, producedEvent);

      //
      if (toConsume == null) {
        return false;
      } else {
        producedEventSize++;
        for (WindowEvent event : toConsume) {
          toConsumeEvents.add(event);
        }
        return true;
      }
    }
  }